python自动化运维之路~DAY6
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
如果你想开发一款游戏,会存在角色的混搭的情况,这个时候“面向对象过程”就出现了,他能轻松的解决这个问题。
一.编程范式
编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 。
一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式(比如说:java),当然也有些语言可以同时支持多种编程范式(Python)。 两种最重要的编程范式分别是面向过程编程和面向对象编程。
二.面向过程编程(Procedural Programming)
Procedural programming uses a list of instructions to tell the computer what to do step-by-step.
面向过程编程依赖 - 你猜到了- procedures,一个procedure包含一组要被进行计算的步骤, 面向过程又被称为top-down languages, 就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
举个典型的面向过程的例子, 数据库备份, 分三步,连接数据库,备份数据库,测试备份文件可用性。
1 def db_conn(): 2 print("connecting db...") 3 4 def db_backup(dbname): 5 print("导出数据库...",dbname) 6 print("将备份文件打包,移至相应目录...") 7 8 def db_backup_test(): 9 print("将备份文件导入测试库,看导入是否成功") 10 11 def main(): 12 db_conn() 13 db_backup('my_db') 14 db_backup_test() 15 16 if __name__ == '__main__': 17 main() 18 19 20 "' 21 这样做的问题也是显而易见的,就是如果你要对程序进行修改,对你修改的那部分有依赖的各个部分你都也要跟着修改, 举个例子,如果程序开头你设置了一个变量值 为1 , 但如果其它子过程依赖这个值 为1的变量才能正常运行,那如果你改了这个变量,那这个子过程你也要修改,假如又有一个其它子程序依赖这个子过程 , 那就会发生一连串的影响,随着程序越来越大, 这种编程方式的维护难度会越来越高。 22 所以我们一般认为, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护 的, 那还是用面向对象最方便了。 23 ''' 24
三.面向对象编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
1>.Class 类:一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法(其实类就是一堆函数组成的);
2>.Object 对象:一个对象即是一个类的实例化(就好像是zabbix模板被监控的主机引用的过程)后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同;
3>.Encapsulation 封装:在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法;
4>Inheritance 继承:一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承;
5>.Polymorphism 多态:多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
四.面向对象编程(Object-Oriented Programming )介绍
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Man(object): #定义类名首字母需要大写哟 7 def __init__(self,name,type): #初始化对象(初始化函数又叫构造函数),即一旦调用Man这个类,就会执行以下代码 8 self.name = name #这个步骤是相当于a.name = name,为了就是方便下面的函数去调用他们 9 self.type = type #这个步骤相当于a.type = type 10 def introduce(self): 11 print("[%s] is a good boy!" % self.name) 12 def eat(self,food): 13 print("[%s] eating [%s]"%(self.name,food)) 14 jie = Man("yinzhengjie","goodboy") 15 print(jie.name,jie.type) 16 jie.introduce() 17 jie.eat("apple") 18 19 20 21 #以上代码执行结果如下: 22 yinzhengjie goodboy 23 [yinzhengjie] is a good boy! 24 [yinzhengjie] eating [apple]
五.面向对象的特性
1.什么是特性property(是用来提供接口的一种方式)
property是一种特殊的属性,访问它时会执行函数然后返回值
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 import math 7 class Circle: 8 def __init__(self,radius): #圆的半径radius 9 self.radius=radius 10 @property #其实就是一个装饰器,作用等效于 area = property(area) 11 def area(self): 12 return math.pi * self.radius**2 #计算面积 13 @property #作用等效于 area = property(perimeter) 14 def perimeter(self): 15 return 2*math.pi*self.radius #计算周长 16 17 c=Circle(5) #传入参数半径 18 print(c.radius) #调用的时候就不用调用类里面的方法(函数),直接调用数据属性,不需要调用函数属性 19 print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 20 print(c.perimeter) #同上 21 22 23 #以上代码执行结果如下: 24 5 25 78.53981633974483 26 31.41592653589793
2 .为什么要用property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则,即它是将一个方法变成静态属性。
除此之外,看下
1 ''' 2 ps:面向对象的封装有三种方式: 3 【public】 4 这种其实就是不封装,是对外公开的 5 【protected】 6 这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开 7 【private】 8 这种封装对谁都不公开 9 '''
python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现
使用展示一:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Man(object): 7 name = "ddddd" 8 def __init__(self ,name): 9 self.name = name 10 @classmethod 11 def talk(self): 12 print("%s is talking" % self.name) 13 @property #把eat这个方法变成静态属性 14 def eat(self): 15 print("%s is eating"% self.name) 16 @eat.setter #赋值, 17 def eat(self,num): 18 print("changing status", num) 19 @eat.deleter #删除, 20 def eat(self): 21 print("deleting....eat") 22 d = Man("Yinzhengjie") 23 d.eat #触发 @property 24 d.eat = 1000 #触发 @eat.setter 25 del d.eat #触发 @eat.deleter 26 27 28 #以上代码执行结果如下: 29 Yinzhengjie is eating 30 changing status 1000 31 deleting....eat
使用展示二:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Foo: 7 def __init__(self,val): 8 self.__NAME=val #将所有的数据属性都隐藏起来 9 10 @property 11 def name(self): 12 return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置) 13 14 @name.setter 15 def name(self,value): 16 if not isinstance(value,str): #在设定值之前进行类型检查 17 raise TypeError('%s must be str' %value) 18 self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME 19 20 @name.deleter 21 def name(self): 22 raise TypeError('Can not delete') 23 24 f=Foo('yinzhengjie') 25 print(f.name) 26 # f.name=10 #抛出异常'TypeError: 10 must be str' 27 # del f.name #抛出异常'TypeError: Can not delete' 28 29 #以上代码执行结果如下: 30 yinzhengjie
应用场景:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 8 class Flight(object): 9 def __init__(self, name): 10 self.flight_name = name 11 12 def checking_status(self): 13 print("checking flight %s status " % self.flight_name) 14 return 3 15 16 @property 17 def flight_status(self): 18 status = self.checking_status() 19 if status == 0: 20 print("flight got canceled...") 21 elif status == 1: 22 print("flight is arrived...") 23 elif status == 2: 24 print("flight has departured already...") 25 else: 26 print("cannot confirm the flight status...,please check later") 27 28 @flight_status.setter # 修改 29 def flight_status(self, status): 30 status_dic = { 31 0: "canceled", 32 1: "arrived", 33 2: "departured" 34 } 35 print("\033[31;1mHas changed the flight status to \033[0m", status_dic.get(status)) 36 37 @flight_status.deleter # 删除 38 def flight_status(self): 39 print("status got removed...") 40 41 42 f = Flight("CA980") 43 f.flight_status 44 f.flight_status = 2 # 触发@flight_status.setter 45 del f.flight_status # 触发@flight_status.deleter 46 47 48 49 #以上代码执行结果如下: 50 checking flight CA980 status 51 cannot confirm the flight status...,please check later 52 Has changed the flight status to departured 53 status got removed...
六.封装
封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
学习封装前要掌握的知识点:
1>类变量:既可以作为默认公有属性也可以全局修改或增加新属性。
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 ''' 7 类变量的作用: 8 1>.作为默认公有属性 9 2>.全局修改或增加新属性 10 ''' 11 12 class People(object): 13 nationality = "CN" #类变量 14 def __init__(self,name,age,job): 15 pass 16 p = People("yinzhengjie","25","IT") 17 p.nationality = "JP" #修改类变量 18 print(p.nationality) 19 p2 = People("alex","35","IT") 20 People.weapon = "Nepal Saber" #全局加武器装备尼泊尔军刀,这个就是类变量的应用 21 print(p.weapon) 22 23 24 25 #以上代码执行结果如下: 26 JP 27 Nepal Saber
2>.实例变量(成员属性):其特性和类变量相反,仅仅属于你自己的实例变量。一般构造函数的实例变量都是实例变量,(即每个实例。存在自己内存空间里的属性。)
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 ''' 7 类变量的作用: 8 1>.作为默认公有属性 9 2>.全局修改或增加新属性 10 ''' 11 12 class People(object): 13 nationality = "CN" #类变量 14 def __init__(self,name,age,job): 15 self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。 16 self.age = age 17 self.job = job 18 p = People("yinzhengjie","25","IT") 19 p.hobbie = "basketball" #添加一个实例变量 20 print(p.hobbie) 21 22 23 24 #以上代码执行结果如下: 25 basketball
3>.公有属性:对应的就是类变量的属性
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Man(object): #定义类名首字母需要大写哟 7 age = 25 #类变量,存在内的内存地址里,可以被所有实例共享引用 8 def __init__(self,name,type): #初始化函数(构造函数) 9 self.name = name #这个步骤是相当于jie.name = name,为了就是方便下面的函数去调用他们 10 self.type = type #这个步骤相当于jie.type = type 11 def introduce(self): 12 print("[%s] is a good boy! I have [%s] years old!" % (self.name,self.age)) #注意:“self.age”其实是引用Man中的类变量 13 def eat(self,food): 14 print("[%s] eating [%s]"%(self.name,food)) 15 jie = Man("yinzhengjie","goodboy") #其中“jie”就是Man的实例,整个动作“jie = Man("yinzhengjie","goodboy") ”叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了 16 print(jie.name,jie.type) 17 jie.introduce() 18 jie.eat("apple") 19 jie.name = "尹正杰" #赋值 20 print(jie.name) 21 22 23 #以上代码执行结果如下: 24 yinzhengjie goodboy 25 [yinzhengjie] is a good boy! I have [25] years old! 26 [yinzhengjie] eating [apple] 27 尹正杰
4>.私有属性:表示不想被别人访问到的属性,只能在内部各函数中调用。隐藏一些功能的实现细节,只给外部暴露调用接口
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 ''' 7 类变量的作用: 8 1>.作为默认公有属性 9 2>.全局修改或增加新属性 10 ''' 11 12 class People(object): 13 nationality = "CN" #类变量 14 def __init__(self,name,age,sex): 15 self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。 16 self.age = age 17 self.__sex = sex #定义私有属性,其只能在内部各函数(严格来说我们应该叫“内部方法”)中调用 18 def go_to_toilet(self): 19 if self.__sex == "F": #在内部的方法中是可以成功调用的! 20 print("stand up.....") 21 else: 22 print("蹲着.....") 23 p = People("yinzhengjie","25","F") 24 p.go_to_toilet() 25 # print(p.sex) #外部调用的话会报错!因为私有属性只能内部方法调用,而不能从外部调用! 26 27 28 29 #以上代码执行结果如下: 30 stand up.....
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class People(object): 7 nationality = "CN" #类变量 8 def __init__(self,name,age,sex): 9 self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。 10 self.age = age 11 self.__sex = sex #定义私有属性,其只能在内部各函数(严格来说我们应该叫“内部方法”)中调用 12 def get_sex(self): 13 return self.__sex #将私有属性的值返回 14 p = People("yinzhengjie","25","F") 15 print(p.get_sex()) #外部只能访问,不可以修改哟,(因为不能给方法复制!) 16 17 18 19 20 #以上代码执行结果如下: 21 F
七.继承
1.继承顺序
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class A(object): 7 def test(self): 8 print('from A') 9 10 class B(A): 11 def test(self): 12 print('from B') 13 14 class C(A): 15 def test(self): 16 print('from C') 17 18 class D(B): 19 def test(self): 20 print('from D') 21 22 class E(C): 23 def test(self): 24 print('from E') 25 26 class F(D,E): 27 # def test(self): 28 # print('from F') 29 pass 30 f1=F() 31 f1.test() 32 print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 33 34 ''' 35 新式类继承顺序:F->D->B->E->C->A (广度优先,【不会优先找到最顶级的类】) 36 经典类继承顺序:F->D->B->A->E->C (深度优先) 37 python3中统一都是新式类 38 pyhon2中才分新式类与经典类 39 ''' 40 41 #以上代码执行结果如下: 42 43 from D 44 (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
2.继承原理(python如何实现的继承)
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__ [, , , , , , ]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
3.子类中调用父类方法
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法()
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Vehicle: # 定义交通工具类 7 Country = 'China' 8 9 def __init__(self, name, speed, load, power): 10 self.name = name 11 self.speed = speed 12 self.load = load 13 self.power = power 14 15 def run(self): 16 print('开动啦...') 17 18 class Subway(Vehicle): # 地铁 19 def __init__(self, name, speed, load, power, line): 20 Vehicle.__init__(self, name, speed, load, power) 21 self.line = line 22 23 def run(self): 24 print('地铁%s号线欢迎您' % self.line) 25 Vehicle.run(self) 26 27 line15 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 15) 28 line15.run() 29 30 31 #以上代码执行结果如下: 32 地铁15号线欢迎您 33 开动啦...
方法二:super()
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Vehicle: # 定义交通工具类 7 Country = 'China' 8 9 def __init__(self, name, speed, load, power): 10 self.name = name 11 self.speed = speed 12 self.load = load 13 self.power = power 14 def run(self): 15 print('开动啦...') 16 class Subway(Vehicle): # 地铁 17 def __init__(self, name, speed, load, power, line): 18 # super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) 19 super().__init__(name, speed, load, power) 20 self.line = line 21 def run(self): 22 print('地铁%s号线欢迎您' % self.line) 23 super(Subway, self).run() 24 class Mobike(Vehicle): # 摩拜单车 25 pass 26 line13 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 13) 27 line13.run() 28 29 #以上代码执行结果如下: 30 地铁13号线欢迎您 31 开动啦...
不用super引发的惨案
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 #每个类中都继承了且重写了父类的方法 7 class A: 8 def __init__(self): 9 print('A的构造方法') 10 class B(A): 11 def __init__(self): 12 print('B的构造方法') 13 A.__init__(self) 14 15 16 class C(A): 17 def __init__(self): 18 print('C的构造方法') 19 A.__init__(self) 20 21 22 class D(B,C): 23 def __init__(self): 24 print('D的构造方法') 25 B.__init__(self) 26 C.__init__(self) 27 28 pass 29 f1=D() 30 31 print(D.__mro__) #python2中没有这个属性 32 33 34 35 #以上代码执行结果如下: 36 D的构造方法 37 B的构造方法 38 A的构造方法 39 C的构造方法 40 A的构造方法 41 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 #每个类中都继承了且重写了父类的方法 7 class A: 8 def __init__(self): 9 print('A的构造方法') 10 class B(A): 11 def __init__(self): 12 print('B的构造方法') 13 super(B,self).__init__() 14 15 16 class C(A): 17 def __init__(self): 18 print('C的构造方法') 19 super(C,self).__init__() 20 21 22 class D(B,C): 23 def __init__(self): 24 print('D的构造方法') 25 super(D,self).__init__() 26 27 f1=D() 28 29 print(D.__mro__) #python2中没有这个属性 30 31 32 #以上代码执行结果如下: 33 D的构造方法 34 B的构造方法 35 C的构造方法 36 A的构造方法 37 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class SchoolMember(object): 7 def __init__(self,name,age,sex): 8 self.name = name 9 self.age = age 10 self.sex = sex 11 def tell(self): 12 info = """ 13 --------info of %s------------- 14 name : %s 15 age : %s 16 sex : %s 17 """%(self.name,self.name,self.age,self.sex) 18 19 class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了. 20 def __init__(self,name,age,sex,salary): #初始化函数 21 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量 22 self.salary = salary #表示重新构造函数 23 def teaching(self,course): 24 print("%s is teaching %s"%(self.name,course)) 25 26 t = Teacher("yinzhengjie",25,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。 27 print(t.name) 28 print(t.sex) 29 print(t.salary) 30 31 32 #以上代码执行结果如下: 33 yinzhengjie 34 boy 35 1000
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class SchoolMember(object): 7 members = 0 8 def __init__(self,name,age,sex): 9 self.name = name 10 self.age = age 11 self.sex = sex 12 SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟! 13 print("初始化了一个新学校成员",self.name) 14 def tell(self): 15 info = """ 16 --------info of %s------------- 17 name : %s 18 age : %s 19 sex : %s 20 """%(self.name,self.name,self.age,self.sex) 21 print(info) 22 class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了. 23 def __init__(self,name,age,sex,salary): #初始化函数 24 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量 25 self.salary = salary #表示重新构造函数 26 def teaching(self,course): 27 print("%s is teaching %s"%(self.name,course)) 28 class Student(SchoolMember): 29 def __init__(self, name, age, sex, salary): # 初始化函数 30 SchoolMember.__init__(self, name, age, sex) 31 def pay_tuition(self, amount): 32 self.paid_tuition = amount 33 print("student %s has paid tution amoint %s" % (self.name, amount)) 34 t = Teacher("yinzhengjie",25,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。 35 s = Student("lifeng","26","M","pys600") 36 t.tell() 37 t.teaching("python") 38 s.pay_tuition(11000) 39 print("目前学校公有[%s]个学生"% SchoolMember.members) 40 41 42 #以上代码执行结果如下: 43 初始化了一个新学校成员 yinzhengjie 44 初始化了一个新学校成员 lifeng 45 46 --------info of yinzhengjie------------- 47 name : yinzhengjie 48 age : 25 49 sex : boy 50 51 yinzhengjie is teaching python 52 student lifeng has paid tution amoint 11000 53 目前学校公有[2]个学生
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 ''' 7 继承的作用: 8 1>.直接调用父类方法; 9 2>.继承父类方法并重构父类方法,先重构,在重构的方法里手动调用父类方法 10 3>.可以定义子类自己的方法; 11 4>.可以析构方法"__del__" 12 ''' 13 class SchoolMember(object): 14 members = 0 15 def __init__(self,name,age,sex): 16 self.name = name 17 self.age = age 18 self.sex = sex 19 SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟! 20 print("初始化了一个新学校成员",self.name) 21 def tell(self): 22 info = """ 23 --------info of %s------------- 24 name : %s 25 age : %s 26 sex : %s 27 """%(self.name,self.name,self.age,self.sex) 28 print(info) 29 def __del__(self): #析构函数(析构方法) 30 print(("注意:[%s]已经被开除了!" % self.name)) 31 SchoolMember.members -= 1 32 class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了. 33 def __init__(self,name,age,sex,salary): #初始化函数 34 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量 35 self.salary = salary #表示重新构造函数 36 def teaching(self,course): 37 print("%s is teaching %s"%(self.name,course)) 38 class Student(SchoolMember): 39 def __init__(self, name, age, sex, salary): # 初始化函数 40 SchoolMember.__init__(self, name, age, sex) 41 def pay_tuition(self, amount): 42 self.paid_tuition = amount 43 print("student %s has paid tution amoint %s" % (self.name, amount)) 44 t = Teacher("yinzhengjie",25,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。 45 s = Student("lifeng","26","M","pys600") 46 s2 = Student("test","22","F","pys16") 47 s3 = Student("test_1","23","F","pys16") 48 del s2 #删除一个实例用del,在实例被销毁后自动执行 49 t.tell() 50 t.teaching("python") 51 s.pay_tuition(11000) 52 print("目前学校公有[%s]个学生"% SchoolMember.members) 53 54 55 56 #以上大米执行结果如下: 57 初始化了一个新学校成员 yinzhengjie 58 初始化了一个新学校成员 lifeng 59 初始化了一个新学校成员 test 60 初始化了一个新学校成员 test_1 61 注意:[test]已经被开除了! 62 63 --------info of yinzhengjie------------- 64 name : yinzhengjie 65 age : 25 66 sex : boy 67 68 yinzhengjie is teaching python 69 student lifeng has paid tution amoint 11000 70 目前学校公有[3]个学生 71 注意:[test_1]已经被开除了! #以为程序结束了,内存会被释放掉,所以在del实例后,会打印以下3行代码。 72 注意:[yinzhengjie]已经被开除了! 73 注意:[lifeng]已经被开除了!
八.组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程。当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class BirthDate: 7 def __init__(self,year,month,day): 8 self.year=year 9 self.month=month 10 self.day=day 11 class Couse: 12 def __init__(self,name,price,period): 13 self.name=name 14 self.price=price 15 self.period=period 16 17 class Teacher: 18 def __init__(self,name,gender): 19 self.name=name 20 self.gender=gender 21 def teach(self): 22 print('teaching') 23 class Professor(Teacher): 24 def __init__(self,name,gender,birth,course): 25 Teacher.__init__(self,name,gender) #直接将Teacher类的方法那来这个类里面用的方式就叫组合 26 self.birth=birth 27 self.course=course 28 29 p1=Professor('egon','male',BirthDate('1992','1','27'),Couse('python','28000','4 months')) 30 print(p1.birth.year,p1.birth.month,p1.birth.day) 31 print(p1.course.name,p1.course.price,p1.course.period) 32 33 34 35 36 #以上代码执行结果如下: 37 1992 1 27 38 python 28000 4 months
九.接口与归一化设计
1.什么是接口
1 =================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java 2 /* 3 * Java的Interface很好的体现了我们前面分析的接口的特征: 4 * 1)是一组功能的集合,而不是一个功能 5 * 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作 6 * 3)接口只定义函数,但不涉及函数实现 7 * 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */ 8 9 package com.oo.demo; 10 public interface IAnimal { 11 public void eat(); 12 public void run(); 13 public void sleep(); 14 public void speak(); 15 } 16 17 =================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口 18 package com.oo.demo; 19 public class Pig implements IAnimal{ //如下每个函数都需要详细实现 20 public void eat(){ 21 System.out.println("Pig like to eat grass"); 22 } 23 24 public void run(){ 25 System.out.println("Pig run: front legs, back legs"); 26 } 27 28 public void sleep(){ 29 System.out.println("Pig sleep 16 hours every day"); 30 } 31 32 public void speak(){ 33 System.out.println("Pig can not speak"); } 34 } 35 36 =================第三部分:Person2.java 37 /* 38 *实现了IAnimal的“人”,有几点说明一下: 39 * 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样 40 * 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */ 41 42 package com.oo.demo; 43 public class Person2 implements IAnimal { 44 public void eat(){ 45 System.out.println("Person like to eat meat"); 46 } 47 48 public void run(){ 49 System.out.println("Person run: left leg, right leg"); 50 } 51 52 public void sleep(){ 53 System.out.println("Person sleep 8 hours every dat"); 54 } 55 56 public void speak(){ 57 System.out.println("Hellow world, I am a person"); 58 } 59 } 60 61 =================第四部分:Tester03.java 62 package com.oo.demo; 63 64 public class Tester03 { 65 public static void main(String[] args) { 66 System.out.println("===This is a person==="); 67 IAnimal person = new Person2(); 68 person.eat(); 69 person.run(); 70 person.sleep(); 71 person.speak(); 72 73 System.out.println("\n===This is a pig==="); 74 IAnimal pig = new Pig(); 75 pig.eat(); 76 pig.run(); 77 pig.sleep(); 78 pig.speak(); 79 } 80 } 81 82 java中的interface
继承有两种用途:
1>:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
2>:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能
class Interface:#定义接口Interface类来模仿接口的概念,python中压根就没有interface关键字来定义一个接口。 def read(self): #定接口函数read pass def write(self): #定义接口函数write pass class Txt(Interface): #文本,具体实现read和write def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(Interface): #磁盘,具体实现read和write def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法')
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
在python中根本就没有一个叫做interface的关键字,上面的代码只是看起来像接口,其实并没有起到接口的作用,子类完全可以不用去实现接口 ,如果非要去模仿接口的概念,可以借助第三方模块:http://pypi.python.org/pypi/zope.interface中twisted的twisted\internet\interface.py里使用zope.interface
文档https://zopeinterface.readthedocs.io/en/latest/
设计模式:https://github.com/faif/python-patterns
2. 为何要用接口
接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。归一化,让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
比如:我们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由二者分别产生一只老鼠和一只松鼠送到你面前,即便是你分别不到底哪只是什么鼠你肯定知道他俩都会跑,都会吃,都能呼吸。再比如:我们有一个汽车接口,里面定义了汽车所有的功能,然后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,大家只需要学会了怎么开汽车,那么无论是本田,还是奥迪,还是大众我们都会开了,开的时候根本无需关心我开的是哪一类车,操作手法(函数调用)都一样
十.抽象类
什么是抽象类:
与java一样,python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
为什么要有抽象类:
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。 比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
其实抽象类说直白了就是让你们的程序更加的规范化:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 #一切皆文件 7 import abc #利用abc模块实现抽象类 8 9 class All_file(metaclass=abc.ABCMeta): 10 all_type='file' 11 @abc.abstractmethod #定义抽象方法,无需实现功能,只要这个类中的方法中含有该装饰器“abc.abstractmethod”,所以继承该类的人都要重写该方法! 12 def read(self): 13 '子类必须定义读功能' 14 pass 15 @abc.abstractmethod #定义抽象方法,无需实现功能 16 def write(self): 17 '子类必须定义写功能' 18 pass 19 # class Txt(All_file): 20 # pass 21 # 22 # t1=Txt() #报错,子类没有定义抽象方法 23 24 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 25 def read(self): 26 print('文本数据的读取方法') 27 28 def write(self): 29 print('文本数据的读取方法') 30 31 class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 32 def read(self): 33 print('硬盘数据的读取方法') 34 35 def write(self): 36 print('硬盘数据的读取方法') 37 38 class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 39 def read(self): 40 print('进程数据的读取方法') 41 42 def write(self): 43 print('进程数据的读取方法') 44 45 wenbenwenjian=Txt() 46 47 yingpanwenjian=Sata() 48 49 jinchengwenjian=Process() 50 51 #这样大家都是被归一化了,也就是一切皆文件的思想 52 wenbenwenjian.read() 53 yingpanwenjian.write() 54 jinchengwenjian.read() 55 56 print(wenbenwenjian.all_type) 57 print(yingpanwenjian.all_type) 58 print(jinchengwenjian.all_type) 59 60 61 #以上代码执行结果如下: 62 文本数据的读取方法 63 硬盘数据的读取方法 64 进程数据的读取方法 65 file 66 file 67 file
抽象类与接口
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
十一.多态
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 #序列内形的不同形态如下:分别有字符串,元组和列表,这就体现了同一种食物的不同形态,简称多态。 7 name = "尹正杰" 8 tuple_test = ("yinzhengjie","123") 9 list_test = ["尹正杰","25","175","北京","朝阳区"] 10 11 12 #多态性:一个入口函数有多种实现方式 13 def fun(obj): #"obj"这个参数就体现多态性。原理就是传递进来的对象都有相同(同名)的“__len__()”方法,只不过同一个方法会执行不同的功能而已。 14 print(obj.__len__()) #相当于len(obj) 15 16 fun(name) 17 fun(tuple_test) 18 fun(list_test) 19 20 21 #以上代码的执行结果如下: 22 3 23 2 24 5
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 #多态如下: 7 class Animal: 8 def talk(self): 9 pass 10 class People(Animal): 11 def talk(self): 12 print("say hello") 13 14 class Pig(Animal): 15 def talk(self): 16 print("say heng····heng") 17 18 class Dog(Animal): 19 def talk(self): 20 print("say wang···wang") 21 22 class Cat(Animal): 23 def talk(self): 24 print("miao~miao~miao~") 25 26 p1 = People() #这就是一个实例,其本质上就是将“p1 = People()”传递给“self” 27 pig1 = Pig() 28 D1 = Dog() 29 c = Cat() 30 31 #多态性: 32 def fun(obj): 33 obj.talk() 34 fun(p1) 35 fun(pig1) 36 fun(D1) 37 fun(c) 38 39 #以上代码执行结果如下: 40 say hello 41 say heng····heng 42 say wang···wang 43 miao~miao~miao~
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 #定义多态如下: 7 class Animal(object): 8 def __init__(self, name): # Constructor of the class 9 self.name = name 10 11 def talk(self): # Abstract method, defined by convention only 12 raise NotImplementedError("Subclass must implement abstract method") 13 class Cat(Animal): 14 def talk(self): 15 print('【%s】: 喵喵喵!' % self.name) 16 class Dog(Animal): 17 def talk(self): 18 print('【%s】: 汪!汪!汪!' % self.name) 19 #定义多态性: 20 def func(obj): # 一个接口,多种形态 21 obj.talk() 22 c1 = Cat('hello kitty') 23 d1 = Dog('犬夜叉') 24 25 func(c1) 26 func(d1) 27 28 #以上代码执行结果如下: 29 【hello kitty】: 喵喵喵! 30 【犬夜叉】: 汪!汪!汪!
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 ''' 7 继承的作用: 8 1>.直接调用父类方法; 9 2>.继承父类方法并重构父类方法,先重构,在重构的方法里手动调用父类方法 10 3>.可以定义子类自己的方法; 11 4>.可以析构方法"__del__" 12 ''' 13 class SchoolMember(object): 14 members = 0 15 test = 111111 16 print("in SchoolMember ") 17 def __init__(self,name,age,sex): 18 self.name = name 19 self.age = age 20 self.sex = sex 21 SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟! 22 print("初始化了一个新学校成员",self.name) 23 class Course(object): #定义一个类其实也可以不用谢初始化函数用,只写类变量也可以 24 test = 666666 25 print("in Course ") 26 Course_name = "python自动化" 27 period = "9m" 28 outtline = "test" 29 class Student(SchoolMember,Course): #继承了2个父类,继承顺序优先级由左往右依次递减 30 def __init__(self, name, age, sex, salary): # 初始化函数 31 SchoolMember.__init__(self, name, age, sex) 32 def pay_tuition(self, amount): 33 self.paid_tuition = amount #存在实例里,方便其他函数(方法)调用。 34 print("student %s has paid tution amoint %s" % (self.name, amount)) 35 s = Student("yinzhengjie","25","M","pys11000") 36 print(s.Course_name,Course.outtline) 37 print(s.test) 38 39 40 #以上代码执行结果如下: 41 in SchoolMember 42 in Course 43 初始化了一个新学校成员 yinzhengjie 44 python自动化 test 45 111111
通常情况下,在类中定义的所有函数(注意了,这里说的就是所有,跟self啥的没关系,self也只是一个再普通不过的参数而已)都是对象的绑定方法,对象在调用绑定方法时会自动将自己作为参数传递给方法的第一个参数。除此之外还有两种常见的方法:静态方法和类方法,二者是为类量身定制的,但是实例非要使用,也不会报错,后续将介绍。
1.静态方法
是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法。静态方法既不属于类也不属于实例。与类只是属于名义上的归属关系
class Foo: def spam(x,y,z): #类中的一个函数,千万不要懵逼,self和x啥的没有不同都是参数名 print(x,y,z) spam=staticmethod(spam) #把spam函数做成静态方法
基于之前所学装饰器的知识,@staticmethod 等同于spam=staticmethod(spam),于是
class Foo: @staticmethod #装饰器 def spam(x,y,z): print(x,y,z)
使用演示
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 class Foo: 8 @staticmethod #装饰器 9 def spam(x,y,z): 10 print(x,y,z) 11 12 13 print(type(Foo.spam)) #类型本质就是函数 14 Foo.spam(100,200,300) #调用函数应该有几个参数就传几个参数 15 16 f1=Foo() 17 f1.spam(6,6,6) #实例也可以使用,但通常静态方法都是给类用的,实例在使用时丧失了自动传值的机制 18 19 20 21 #以上代码执行结果如下: 22 <class 'function'> 23 100 200 300 24 6 6 6
使用展示二:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 class food(object): 8 def __init__(self,name): 9 self.name = name 10 print("good boy!") 11 @staticmethod 12 def eat(): 13 print("你吃了吗?") 14 15 test = food("rice") 16 test.eat() 17 food.eat() #不用实例化就可以直接调用静态方法,所以说静态方法既不属于类也不属于实例。与类只是属于名义上的归属关系,两者的唯一联系就是在调用静态方法(eat)的时候必须先调用该类(food). 18 19 #以上代码执行结果如下: 20 good boy! 21 你吃了吗? 22 你吃了吗?
应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :yinzhengjie #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ #EMAIL:[email protected] import time class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回 @staticmethod def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday) a=Date('2014',7,18) #自己定义时间 b=Date.now() #采用当前时间 c=Date.tomorrow() #采用明天的时间 print(a.year,a.month,a.day) print(b.year,b.month,b.day) print(c.year,c.month,c.day) #以上代码执行结果如下: 2014 7 18 2017 5 9 2017 5 10
2.类方法
类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python为我们内置了函数classmethod来把类中的函数定义成类方法,类方法属于类不属于实例。因为类方法能访问类变量,但是无法访问实例变量。
使用展示一:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 8 class A: 9 x=1 10 @classmethod 11 def test(cls): 12 print(cls,cls.x) 13 14 class B(A): 15 x=100 16 17 B.test() 18 print(A) 19 20 21 #以上代码执行结果如下: 22 <class '__main__.B'> 100 23 <class '__main__.A'>
使用展示二:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 class Man(object): 8 name = "尹正杰" #定义一个类变量 9 def __init__(self,name): 10 self.name = name #实例变量 11 print("good boy!") 12 @classmethod #类方法 13 def talk(self): 14 print("%s is talking"% self.name) #类方法只能调用class中的方法或变量,但是无法调用某个方法下的实例变量。 15 16 @staticmethod #静态方法 17 def eat(): 18 print("你吃了吗?") 19 20 a = Man("yinzhengjie") 21 a.talk() 22 23 24 25 26 #以上代码执行结果如下: 27 good boy! 28 尹正杰 is talking
应用场景:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 import time 8 class Date: 9 def __init__(self,year,month,day): 10 self.year=year 11 self.month=month 12 self.day=day 13 @staticmethod 14 def now(): 15 t=time.localtime() 16 return Date(t.tm_year,t.tm_mon,t.tm_mday) 17 18 class EuroDate(Date): 19 def __str__(self): 20 return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) 21 22 e=EuroDate.now() 23 print(e) #我们的意图是想触发EuroDate.__str__,但是结果为“<__main__.Date object at 0x029F38D0>” 24 25 26 27 #以上代码执行结果如下: 28 <__main__.Date object at 0x029838D0>
原因:
因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 import time 8 class Date: 9 def __init__(self,year,month,day): 10 self.year=year 11 self.month=month 12 self.day=day 13 # @staticmethod 14 # def now(): 15 # t=time.localtime() 16 # return Date(t.tm_year,t.tm_mon,t.tm_mday) 17 18 @classmethod #改成类方法 19 def now(cls): 20 t=time.localtime() 21 return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化 22 23 class EuroDate(Date): 24 def __str__(self): 25 return 'year:%s month:%s day:%s' %(self.year,self.month,self.day) 26 27 e=EuroDate.now() 28 print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿 29 30 ''' 31 注意: 32 静态方法和类方法虽然是给类准备的,但是如果实例去用,也是可以用的,只不过实例去调用的时候容易让人混淆,不知道你要干啥 33 ''' 34 x=e.now() #通过实例e去调用类方法也一样可以使用,静态方法也一样 35 print(x) 36 37 38 #以上代码执行结果如下: 39 year:2017 month:5 day:9 40 year:2017 month:5 day:9
注意:以上所述都是Python都是class语音上的一些特性,你可以去用它,但是不一样要一定去用它!那么如何面向对象开发程序呢?所以下面基本上是写一个程序你需要考虑的因素,弄明白了需求才去用以上的方法去解决问题,所以下面的才是干货!必看哟~
十三.面向对象的软件开发
很多人在学完了python的class机制之后,遇到一个生产中的问题,还是会懵逼,这其实太正常了,因为任何程序的开发都是先设计后编程,python的class机制只不过是一种编程方式,如果你硬要拿着class去和你的问题死磕,变得更加懵逼都是分分钟的事,在以前,软件的开发相对简单,从任务的分析到编写程序,再到程序的调试,可以由一个人或一个小组去完成。但是随着软件规模的迅速增大,软件任意面临的问题十分复杂,需要考虑的因素太多,在一个软件中所产生的错误和隐藏的错误、未知的错误可能达到惊人的程度,这也不是在设计阶段就完全解决的。
所以软件的开发其实一整套规范,我们所学的只是其中的一小部分,一个完整的开发过程,需要明确每个阶段的任务,在保证一个阶段正确的前提下再进行下一个阶段的工作,称之为软件工程
面向对象的软件工程包括下面几个部:
1.面向对象分析(object oriented analysis ,OOA)
软件工程中的系统分析阶段,要求分析员和用户结合在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做什么,而不是怎么去做。面向对象的分析要按照面向对象的概念和方法,在对任务的分析中,从客观存在的事物和事物之间的关系,贵南出有关的对象(对象的‘特征’和‘技能’)以及对象之间的联系,并将具有相同属性和行为的对象用一个类class来标识。
建立一个能反映这是工作情况的需求模型,此时的模型是粗略的。
2.面向对象设计(object oriented design,OOD)
根据面向对象分析阶段形成的需求模型,对每一部分分别进行具体的设计。
首先是类的设计,类的设计可能包含多个层次(利用继承与派生机制)。然后以这些类为基础提出程序设计的思路和方法,包括对算法的设计。
在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如伪代码或流程图)来描述
3.面向对象编程(object oriented programming,OOP)
根据面向对象设计的结果,选择一种计算机语言把它写成程序,可以是python
4.面向对象测试(object oriented test,OOT)
在写好程序后交给用户使用前,必须对程序进行严格的测试,测试的目的是发现程序中的错误并修正它。
面向对的测试是用面向对象的方法进行测试,以类作为测试的基本单元。
5.面向对象维护(object oriendted soft maintenance,OOSM)
正如对任何产品都需要进行售后服务和维护一样,软件在使用时也会出现一些问题,或者软件商想改进软件的性能,这就需要修改程序。
由于使用了面向对象的方法开发程序,使用程序的维护比较容易。
因为对象的封装性,修改一个对象对其他的对象影响很小,利用面向对象的方法维护程序,大大提高了软件维护的效率,可扩展性高。
在面向对象方法中,最早发展的肯定是面向对象编程(OOP),那时OOA和OOD都还没有发展起来,因此程序设计者为了写出面向对象的程序,还必须深入到分析和设计领域,尤其是设计领域,那时的OOP实际上包含了现在的OOD和OOP两个阶段,这对程序设计者要求比较高,许多人感到很难掌握。
现在设计一个大的软件,是严格按照面向对象软件工程的5个阶段进行的,这个5个阶段的工作不是由一个人从头到尾完成的,而是由不同的人分别完成,这样OOP阶段的任务就比较简单了。程序编写者只需要根据OOd提出的思路,用面向对象语言编写出程序既可。
在一个大型软件开发过程中,OOP只是很小的一个部分。
对于全栈开发的你来说,这五个阶段都有了,对于简单的问题,不必严格按照这个5个阶段进行,往往由程序设计者按照面向对象的方法进行程序设计,包括类的设计和程序的设计
十四.小白容易犯的错误
1.面向对象的程序设计看起来高大上,所以我在编程时就应该保证通篇class,这样写出的程序一定是好的程序(面向对象只适合那些可扩展性要求比较高的场景)
2.很多人喜欢说面向对象三大特性(这是从哪传出来的,封装,多态,继承?漏洞太多太多,好吧暂且称为三大特性),那么我在基于面向对象编程时,我一定要让我定义的类中完整的包含这三种特性,这样写肯定是好的程序
好家伙,我说降龙十八掌有十八掌,那么你每次跟人干仗都要从第一掌打到第18掌这才显得你会了是么,我来一万个人你需要打10000*18掌对么,傻叉
3.类有类属性,实例有实例属性,所以我们在定义class时一定要定义出那么几个类属性,想不到怎么办,那就使劲的想,定义的越多越牛逼
这就犯了一个严重的错误,程序越早面向对象,死的越早,为啥面向对象,因为我们要将数据与功能结合到一起,程序整体的结构都没有出来,或者说需要考虑的问题你都没有搞清楚个八九不离十,你就开始面向对象了,这就导致了,你在那里干想,自以为想通了,定义了一堆属性,结果后来又都用不到,或者想不通到底应该定义啥,那就一直想吧,想着想着就疯了。
你见过哪家公司要开发一个软件,上来就开始写,肯定是频繁的开会讨论计划,请看第八节
4.既然这么麻烦,那么我彻底解脱了,我们不要用面向对象编程了,你啊,你有大才,你能成事啊,傻叉。
1.抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。
2.封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
3.合成
合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。
4.派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
5.泛化/特化
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
6.多态与多态性
多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气
多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样
7.自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__
8.类的特殊成员方法
A>."__doc__"表示类的描述信息
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 8 class Foo: 9 """ 未成年勿进,老司机要开车了""" 10 def func(self): 11 pass 12 13 print(Foo.__doc__) 14 15 16 17 #以上代码执行结果如下: 18 未成年勿进,老司机要开车了
B>.__module__ 和 __class__
假设存在以下目录结构:
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Name: 7 def __init__(self): 8 self.name = 'yinzhengjie'
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 8 from lib.test import Name 9 ''' 10 __module__ 表示当前操作的对象在那个模块. 11 12 __class__ 表示当前操作的对象的类是什么. 13 ''' 14 15 obj = Name() 16 print(obj.__module__) # 输出模块 17 print(obj.__class__) # 输出类 18 19 20 #以上代码执行结果如下: 21 lib.test 22 <class 'lib.test.Name'>
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 import importlib 8 ''' 9 导入模块,我们可以花式玩,请往下看。 10 ''' 11 12 13 #导入方法一:这是puthon解释器自己内部用的。 14 __import__('lib.test') 15 16 #导入方法二:与上面这局效果一样,官方建议用这个。 17 importlib.import_module('lib.test')
C>.__init__ 构造方法,通过类创建对象时,自动触发执行。
D.__del__析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
E. __call__ 对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Foo: 7 def __init__(self): 8 pass 9 10 def __call__(self, *args, **kwargs): 11 print("My name is yinzhengjie") 12 13 a = Foo() #对这个类进行实例化。 14 a() #调用该实例。 15 16 17 18 #以上代码执行结果如下: 19 My name is yinzhengjie
F. __dict__ 查看类或对象中的所有成员
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Province: 7 country = 'China' 8 9 def __init__(self, name, count): 10 self.name = name 11 self.count = count 12 13 def func(self, *args, **kwargs): 14 print('func') 15 16 17 # 获取类的成员,即:静态字段、方法、 18 print(Province.__dict__) 19 20 obj1 = Province('陕西', "1924km") 21 print(obj1.__dict__) # 获取 对象obj1 的成员 22 23 obj2 = Province('河北', "365km") 24 print(obj2.__dict__) # 获取 对象obj2 的成员 25 26 27 28 #以上代码执行结果如下:
G.__str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Foo: 7 def __str__(self): 8 return 'yinzhengjie is good boy !' 9 10 11 obj = Foo() 12 print(obj) 13 14 15 16 #以上代码执行结果如下: 17 yinzhengjie is good boy !
H.__getitem__、__setitem__、__delitem__
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Foo(object): 7 def __getitem__(self, key): 8 print('__getitem__', key) 9 10 def __setitem__(self, key, value): 11 print('__setitem__', key, value) 12 13 def __delitem__(self, key): 14 print('__delitem__', key) 15 16 17 obj = Foo() 18 19 result = obj['Boy'] # 自动触发执行 __getitem__ 20 obj['k2'] = 'Yinzhengjie' # 自动触发执行 __setitem__ 21 del obj['123'] 22 23 24 #以上代码执行结果如下: 25 __getitem__ Boy 26 __setitem__ k2 Yinzhengjie 27 __delitem__ 123
I.__new__ 和 __metaclass__
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class Foo(object): 7 def __init__(self, name): 8 self.name = name 9 f = Foo("yinzhengjie") 10 ''' 11 注意: 12 上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python 13 中一切事物都是对象。如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是 14 通过执行某个类的 构造方法 创建。 15 ''' 16 print(type(f)) 17 print(type(Foo)) 18 19 20 以上代码执行结果如下: 21 <class '__main__.Foo'> #输出:表示,obj 对象由Foo类创建。 22 <class 'type'> # 输出:表示,Foo类对象由 type 类创建。
所以,f对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 #那么,创建类就可以有两种方式: 8 #a).普通方式 9 class Foo(object): 10 def func(self): 11 print('hello Yinzhengjie') 12 print(Foo) 13 print(type(Foo)) 14 print("*"*50,"我是分割线","*"*50) 15 #b).特殊方式 16 def func(self,name): 17 print('hello',name) 18 19 f = type('Test', (object,), {'func': func}) 20 # type第一个参数:类名 21 # type第二个参数:当前类的基类,也就是第一个参数的类名的父类。 22 # type第三个参数:类的成员(将其他函数封装进去) 23 print(f) 24 print(type(f)) 25 print(dir(f)) #查看“f”类有哪些可用的方法 26 f_obj = f() 27 f_obj.func("尹正杰") 28 29 30 31 32 #以上代码执行结果如下: 33 <class '__main__.Foo'> 34 <class 'type'> 35 ************************************************** 我是分割线 ************************************************** 36 <class '__main__.Test'> 37 <class 'type'> 38 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'func'] 39 hello 尹正杰
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 7 def func(self): 8 print("hello %s"%self.name) 9 10 def __init__(self,name,age): 11 self.name = name 12 self.age = age 13 Foo = type('Foo',(object,),{'func':func,'__init__':__init__}) 14 15 f = Foo("Yinzhengjie",25) 16 f.func() 17 18 19 #以上代码执行结果如下: 20 hello Yinzhengjie
So ,孩子记住,类 是由 type 类实例化产生
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/ 5 #EMAIL:[email protected] 6 class MyType(type): 7 def __init__(self,*args,**kwargs): 8 9 print("Mytype __init__",*args,**kwargs) 10 11 def __call__(self, *args, **kwargs): 12 print("Mytype __call__", *args, **kwargs) 13 obj = self.__new__(self) 14 print("obj ",obj,*args, **kwargs) 15 print(self) 16 self.__init__(obj,*args, **kwargs) 17 return obj 18 19 def __new__(cls, *args, **kwargs): 20 print("Mytype __new__",*args,**kwargs) 21 return type.__new__(cls, *args, **kwargs) 22 23 print("*"*50,"我是分割线","*"*50) 24 class Foo(object,metaclass=MyType): 25 26 27 def __init__(self,name): 28 self.name = name 29 30 print("Foo __init__") 31 32 def __new__(cls, *args, **kwargs): 33 print("Foo __new__",cls, *args, **kwargs) 34 return object.__new__(cls) 35 36 f = Foo("Yinzhengjie") 37 print("对象是:",f) 38 print("尹正杰",f.name) 39 40 41 以上代码执行结果如下: 42 ************************************************** 我是分割线 ************************************************** 43 Mytype __new__ Foo (<class 'object'>,) {'__init__':__init__ at 0x006C0078>, '__new__': __new__ at 0x006C00C0>, '__qualname__': 'Foo', '__module__': '__main__'} 44 Mytype __init__ Foo (<class 'object'>,) {'__init__': __init__ at 0x006C0078>, '__new__': __new__ at 0x006C00C0>, '__qualname__': 'Foo', '__module__': '__main__'} 45 Mytype __call__ Yinzhengjie 46 Foo __new__ <class '__main__.Foo'> 47 obj <__main__.Foo object at 0x006BBC50> Yinzhengjie 48 <class '__main__.Foo'> 49 Foo __init__ 50 对象是: <__main__.Foo object at 0x006BBC50> 51 尹正杰 Yinzhengjie
类的生成 调用 顺序依次是 __new__ --> __init__ --> __call__
metaclass 详解文章:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 得票最高那个答案写的非常好