写了这么多python 代码,也常用的类和对象,这里准备系统的对python的面向对象编程做以下介绍。

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)

面向对象:对函数进行分类和封装,让开发“更快更好更强...”

 

创建类和对象

面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。

  类就是一个模板,模板里可以包含多个函数,函数里实现一些功能

  对象则是根据模板创建的实例,通过实例对象可以执行类中的函数

创建一个类



class EmployeeInfo():
'创建一个员工信息类' # 类文档字符串 位置在类名下单引号中, 类的帮助信息可以通过ClassName.__doc__查看。

#class_suite 类体 由类成员,方法,数据属性组成。

print EmployeeInfo.__doc__ # 创建一个员工信息类


 

应用实例



# 创建一个类,应用实例

class EmployeeInfo():
'员工信息类'

# 定义员工数量,初始化为0 ,每实例化一个对象时,数量增 1
empCount = 0

def __init__(self,name,age): # 类中的函数第一个参数必须是self(详细见:类的三大特性之封装)类中定义的函数叫做 “方法”

self.name = name
self.age = age
EmployeeInfo.empCount += 1

def displayEmployeeInfo(self):

print "员工姓名:",self.name,"员工年龄:",self.age,"员工序号:",self.empCount


emp1 = EmployeeInfo("Tester",18)

emp1.displayEmployeeInfo() # 输出结果 员工姓名: Tester 员工年龄: 18 员工序号: 1

emp2 = EmployeeInfo("Manager",19)

emp2.displayEmployeeInfo() # 员工姓名: Manager 员工年龄: 19 员工序号: 2


 

 

  • empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。
  • 第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法(可以不写,根据实际需要自己定义需要的构造函数)。
  • self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
注意:self 代表的是一个实例,如果使用self.变量,标识的是实例化的变量,如果要使用类变量,必须使用,类名.变量。如:EmployeeInfo.empCount += 1。

self代表类的实例,而非类

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。



# 创建一个测试类

class Test:
'创建一个测试类'
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt()


以上实例执行结果为:



<__main__.Test instance at 0x00000000026CB088>
__main__.Test


从执行结果可以很明显的看出,self 代表的是类的实例,代表当前实例对象的地址,而self.__class__ 则指向该类。

 

类的属性

你也可以使用以下函数的方式来访问属性:

  • getattr(obj, name[, default]) : 访问对象的属性。
  • hasattr(obj,name) : 检查是否存在一个属性。
  • setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
  • delattr(obj, name) : 删除属性。

应用举例



# 如果存在 'age' 属性返回 True。
print hasattr(emp1,'age') # True
# 返回 'age' 属性的值
print getattr(emp1,'age') # 18
# 添加属性 'num' 值为 1
print setattr(emp1,'num',1) # None
# 返回 'num' 属性的值
print getattr(emp1,'num') # 1
# 删除属性 'age'
print delattr(emp1,'num') # None

print getattr(emp1,'num') # 返回为空


 

Python内置类属性

  • __dict__ : 类的属性(包含一个字典,由类的数据属性组成)
  • __doc__ :类的文档字符串
  • __name__: 类名
  • __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
  • __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)



# 示例
print "EmployeeInfo.__doc__:", EmployeeInfo.__doc__
print "EmployeeInfo.__name__:", EmployeeInfo.__name__
print "EmployeeInfo.__module__:", EmployeeInfo.__module__
print "EmployeeInfo.__bases__:", EmployeeInfo.__bases__
print "EmployeeInfo.__dict__:", EmployeeInfo.__dict__


执行结果



EmployeeInfo.__doc__: 员工信息类
EmployeeInfo.__name__: EmployeeInfo
EmployeeInfo.__module__: __main__
EmployeeInfo.__bases__: ()
EmployeeInfo.__dict__: {'__module__': '__main__', 'empCount': 2, '__doc__': '\xe5\x91\x98\xe5\xb7\xa5\xe4\xbf\xa1\xe6\x81\xaf\xe7\xb1\xbb', '__init__': <function __init__ at 0x00000000026F2E48>, 'displayEmployeeInfo': <function displayEmployeeInfo at 0x00000000026F2EB8>}


 

python 析构函数 

析构函数 __del__ ,__del__在对象销毁的时候被调用当对象不再被使用时,__del__方法运行:



class Point:
def __init__( self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print class_name, "销毁"

pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # 打印对象的id
del pt1
del pt2
del pt3


以上实例运行结果如下:



3083401324 3083401324 3083401324
Point 销毁


注意:通常你需要在单独的文件中定义一个类,当然python中一个文件也可以定义多个类,不推荐。

类的继承

继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。

需要注意的地方:继承语法 class 派生类名(基类名)://... 基类名写在括号里,基本类是在类定义的时候,在元组之中指明的。

在python中继承中的一些特点:

  • 1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
  • 2:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数
  • 3:Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。

语法:

派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:



class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite


 

示例



class Parent:        # 定义父类
parentAttr = 100
def __init__(self):
print "调用父类构造函数"

def parentMethod(self):
print '调用父类方法'

def setAttr(self, attr):
Parent.parentAttr = attr

def getAttr(self):
print "父类属性 :", Parent.parentAttr

class Child(Parent): # 定义子类
def __init__(self):
print "调用子类构造方法"

def childMethod(self):
print '调用子类方法'

c = Child() # 实例化子类
c.childMethod() # 调用子类的方法
c.parentMethod() # 调用父类方法
c.setAttr(200) # 再次调用父类的方法 - 设置属性值
c.getAttr() # 再次调用父类的方法 - 获取属性值


以上代码执行结果如下:



调用子类构造方法
调用子类方法
调用父类方法
父类属性 : 200


 

你可以继承多个类



class A:        # 定义类 A
.....

class B: # 定义类 B
.....

class C(A, B): # 继承类 A 和 B
.....


 

那么问题又来了,多继承呢?

  • 是否可以继承多个类
  • 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?

1、Python的类可以继承多个类,Java和C#中则只能继承一个类

2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先

python - 面向对象编程(初级篇)_python

 

  • 当类是经典类时,多继承情况下,会按照深度优先方式查找
  • 当类是新式类时,多继承情况下,会按照广度优先方式查找

经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

 python - 面向对象编程(初级篇)_多继承_02



class D:

def bar(self):
print 'D.bar'


class C(D):

def bar(self):
print 'C.bar'


class B(D):

def bar(self):
print 'B.bar'


class A(B, C):

def bar(self):
print 'A.bar'

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> D --> C
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

经典类多继承


 



class D(object):

def bar(self):
print 'D.bar'


class C(D):

def bar(self):
print 'C.bar'


class B(D):

def bar(self):
print 'B.bar'


class A(B, C):

def bar(self):
print 'A.bar'

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

新式类多继承


 

经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错

新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错

注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

 

你可以使用issubclass()或者isinstance()方法来检测。

  • issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
  • isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

示例



class Parent:  # 定义父类
parentAttr = 100

def __init__(self):
print "调用父类构造函数"

def parentMethod(self):
print '调用父类方法'

def setAttr(self, attr):
Parent.parentAttr = attr

def getAttr(self):
print "父类属性 :", Parent.parentAttr


class Child(Parent): # 定义子类
def __init__(self):
print "调用子类构造方法"

def childMethod(self):
print '调用子类方法'

p = Parent

c = Child() # 实例化子类
c.childMethod() # 调用子类的方法
c.parentMethod() # 调用父类方法
c.setAttr(200) # 再次调用父类的方法 - 设置属性值
c.getAttr() # 再次调用父类的方法 - 获取属性值

print issubclass(Child,Parent) # True issubclass() - 布尔函数判断第一个类是第二个类的子类或者子孙类,语法:issubclass(子类名,父类名)

print isinstance(c,Parent) # True isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。


 

方法重写

 如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:

 示例



class Parent:        # 定义父类
def myMethod(self):
print '调用父类方法'

class Child(Parent): # 定义子类
def myMethod(self):
print '调用子类方法'

c = Child() # 子类实例
c.myMethod() # 子类调用重写方法


执行以上代码输出结果如下:

 



调用子类方法


 

 多态 

 多态(Polymorphism):意味着可以对不同类的对象使用同样的操作,他们会像被“施了魔法一般”工作。

 

 



class F1:
pass


class S1(F1):

def show(self):
print 'S1.show'


class S2(F1):

def show(self):
print 'S2.show'

def Func(obj):
print obj.show()

s1_obj = S1()
Func(s1_obj) # 'S1.show'
s2_obj = S2()
Func(s2_obj)   # 'S2.show'
Python “鸭子类型”


 

 



class A:    
def prt(self):
print "A"

class B(A):
def prt(self):
print "B"

class C(A):
def prt(self):
print "C"

class D(A):
pass

class E:
def prt(self):
print "E"

class F:
pass

def test(arg):
arg.prt()

a = A()
b = B()
c = C()
d = D()
e = E()
f = F()

test(a)
test(b)
test(c)
test(d)
test(e)
test(f)


 

输出结果: 

 



A  
B
C
A
E
Traceback (most recent call last):
File "/Users/shikefu678/Documents/Aptana Studio 3 Workspace/demo/demo.py", line 33, in <module>
test(a),test(b),test(c),test(d),test(e),test(f)
File "/Users/shikefu678/Documents/Aptana Studio 3 Workspace/demo/demo.py", line 24, in test
arg.prt()
AttributeError: F instance has no attribute 'prt'


a,b,c,d都是A类型的变量,所以可以得到预期的效果(从java角度的预期),e并不是A类型的变量但是根据鸭子类型,走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子,e有prt方法,所以在test方法中e就是一个A类型的变量,f没有prt方法,所以f不是A类型的变量。

 


China-测试开发】技术交流群期待你的加入【 193056556

欢迎扫码关注:日益】微信订阅号【 riyi18


python - 面向对象编程(初级篇)_子类_03