python - 面向对象编程(初级篇)
写了这么多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的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先和广度优先
- 当类是经典类时,多继承情况下,会按照深度优先方式查找
- 当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。
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类型的变量。
您的资助是我最大的动力!
金额随意,欢迎来赏!
您的资助是我最大的动力!
金额随意,欢迎来赏!
因为,我的写作热情也离不开您的肯定与支持,感谢您的阅读,我是【Blue·Sky】!
【China-测试开发】技术交流群期待你的加入【 193056556 】
【欢迎扫码关注:日益】微信订阅号【 riyi18 】