python自动化运维之路 --面向对象
作者:程小航
版权声明:原创作品,谢绝转载!否则将追究法律责任。
如果你想开发一款游戏,会存在角色的混搭的情况,这个时候“面向对象过程”就出现了,他能轻松的解决这个问题。
一.编程范式
编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 。
一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式(比如说: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, 就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题 。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
举个典型的面向过程的例子, 数据库备份, 分三步,连接数据库,备份数据库,测试备份文件可用性。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def db_conn(): print("connecting db...") def db_backup(dbname): print("导出数据库...",dbname) print("将备份文件打包,移至相应目录...") def db_backup_test(): print("将备份文件导入测试库,看导入是否成功") def main(): db_conn() db_backup('my_db') db_backup_test() if __name__ == '__main__': main() "' 这样做的问题也是显而易见的,就是如果你要对程序进行修改,对你修改的那部分有依赖的各个部分你都也要跟着修改, 举个例子,如果程序开头你设置了一个变量值 为1 , 但如果其它子过程依赖这个值 为1的变量才能正常运行,那如果你改了这个变量,那这个子过程你也要修改,假如又有一个其它子程序依赖这个子过程 , 那就会发生一连串的影响,随着程序越来越大, 这种编程方式的维护难度会越来越高。 所以我们一般认为, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护 的, 那还是用面向对象最方便了。 '''
三.面向对象编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象的几个核心特性如下
1>.Class 类:一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法(其实类就是一堆函数组成的);
2>.Object 对象:一个对象即是一个类的实例化(就好像是zabbix模板被监控的主机引用的过程)后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同;
3>.Encapsulation 封装:在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法;
4>Inheritance 继承:一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承;
5>.Polymorphism 多态:多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
四.面向对象编程(Object-Oriented Programming )介绍
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class Man(object): #定义类名首字母需要大写哟 def __init__(self,name,type): #初始化对象(初始化函数又叫构造函数),即一旦调用Man这个类,就会执行以下代码 self.name = name #这个步骤是相当于a.name = name,为了就是方便下面的函数去调用他们 self.type = type #这个步骤相当于a.type = type def introduce(self): print("[%s] is a good boy!" % self.name) def eat(self,food): print("[%s] eating [%s]"%(self.name,food)) jie = Man("rianleycheng","goodboy") print(jie.name,jie.type) jie.introduce() jie.eat("apple") #以上代码执行结果如下: rianleycheng goodboy [rianleycheng ] is a good boy! [rianleycheng ] eating [apple]
五.面向对象的特性
1.什么是特性property(是用来提供接口的一种方式)
property是一种特殊的属性,访问它时会执行函数然后返回值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianleycheng #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com import math class Circle: def __init__(self,radius): #圆的半径radius self.radius=radius @property #其实就是一个装饰器,作用等效于 area = property(area) def area(self): return math.pi * self.radius**2 #计算面积 @property #作用等效于 area = property(perimeter) def perimeter(self): return 2*math.pi*self.radius #计算周长 c=Circle(5) #传入参数半径 print(c.radius) #调用的时候就不用调用类里面的方法(函数),直接调用数据属性,不需要调用函数属性 print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) #同上 #以上代码执行结果如下: 5 78.53981633974483 31.41592653589793
2 .为什么要用property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则,即它是将一个方法变成静态属性。
除此之外,看下
''' ps:面向对象的封装有三种方式: 【public】 这种其实就是不封装,是对外公开的 【protected】 这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开 【private】 这种封装对谁都不公开 '''
python并没有在语法上把它们三个内建到自己的class机制中,在C++里一般会将所有的所有的数据都设置为私有的,然后提供set和get方法(接口)去设置和获取,在python中通过property方法可以实现
使用展示一:
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianleycheng #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class Man(object): name = "ddddd" def __init__(self ,name): self.name = name @classmethod def talk(self): print("%s is talking" % self.name) @property #把eat这个方法变成静态属性 def eat(self): print("%s is eating"% self.name) @eat.setter #赋值, def eat(self,num): print("changing status", num) @eat.deleter #删除, def eat(self): print("deleting....eat") d = Man("rianleycheng") d.eat #触发 @property d.eat = 1000 #触发 @eat.setter del d.eat #触发 @eat.deleter #以上代码执行结果如下: rianleycheng is eating changing status 1000 deleting....eat
使用展示二:
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianleycheng #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class Foo: def __init__(self,val): self.__NAME=val #将所有的数据属性都隐藏起来 @property def name(self): return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置) @name.setter def name(self,value): if not isinstance(value,str): #在设定值之前进行类型检查 raise TypeError('%s must be str' %value) self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME @name.deleter def name(self): raise TypeError('Can not delete') f=Foo('rianley') print(f.name) # f.name=10 #抛出异常'TypeError: 10 must be str' # del f.name #抛出异常'TypeError: Can not delete' #以上代码执行结果如下: rianley
应用场景
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianleycheng #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class Flight(object): def __init__(self, name): self.flight_name = name def checking_status(self): print("checking flight %s status " % self.flight_name) return 3 @property def flight_status(self): status = self.checking_status() if status == 0: print("flight got canceled...") elif status == 1: print("flight is arrived...") elif status == 2: print("flight has departured already...") else: print("cannot confirm the flight status...,please check later") @flight_status.setter # 修改 def flight_status(self, status): status_dic = { 0: "canceled", 1: "arrived", 2: "departured" } print("\033[31;1mHas changed the flight status to \033[0m", status_dic.get(status)) @flight_status.deleter # 删除 def flight_status(self): print("status got removed...") f = Flight("CA980") f.flight_status f.flight_status = 2 # 触发@flight_status.setter del f.flight_status # 触发@flight_status.deleter #以上代码执行结果如下: checking flight CA980 status cannot confirm the flight status...,please check later Has changed the flight status to departured status got removed...
六.封装
封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
学习封装前要掌握的知识点:
1>类变量:既可以作为默认公有属性也可以全局修改或增加新属性。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com ''' 类变量的作用: 1>.作为默认公有属性 2>.全局修改或增加新属性 ''' class People(object): nationality = "CN" #类变量 def __init__(self,name,age,job): pass p = People("rianleycheng","21","IT") p.nationality = "JP" #修改类变量 print(p.nationality) p2 = People("egon","18","IT") People.weapon = "Nepal Saber" #全局加武器装备尼泊尔军刀,这个就是类变量的应用 print(p.weapon) #以上代码执行结果如下: JP Nepal Saber
2>.实例变量(成员属性):其特性和类变量相反,仅仅属于你自己的实例变量。一般构造函数的实例变量都是实例变量,(即每个实例。存在自己内存空间里的属性。)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com ''' 类变量的作用: 1>.作为默认公有属性 2>.全局修改或增加新属性 ''' class People(object): nationality = "CN" #类变量 def __init__(self,name,age,job): self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。 self.age = age self.job = job p = People("rianleycheng","21","IT") p.hobbie = "basketball" #添加一个实例变量 print(p.hobbie) #以上代码执行结果如下: basketball
3>.公有属性:对应的就是类变量的属性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianleycheng #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class Man(object): #定义类名首字母需要大写哟 age = 25 #类变量,存在内的内存地址里,可以被所有实例共享引用 def __init__(self,name,type): #初始化函数(构造函数) self.name = name #这个步骤是相当于jie.name = name,为了就是方便下面的函数去调用他们 self.type = type #这个步骤相当于jie.type = type def introduce(self): print("[%s] is a good boy! I have [%s] years old!" % (self.name,self.age)) #注意:“self.age”其实是引用Man中的类变量 def eat(self,food): print("[%s] eating [%s]"%(self.name,food)) jie = Man("rianleycheng","goodboy") #其中“jie”就是Man的实例,整个动作“jie = Man("rianleycheng","goodboy") ”叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了 print(jie.name,jie.type) jie.introduce() jie.eat("apple") jie.name = "程小航" #赋值 print(jie.name) #以上代码执行结果如下: rianleycheng goodboy [rianleycheng] is a good boy! I have [21] years old! [rianleycheng] eating [apple] 程小航
4>.私有属性:表示不想被别人访问到的属性,只能在内部各函数中调用。隐藏一些功能的实现细节,只给外部暴露调用接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com ''' 类变量的作用: 1>.作为默认公有属性 2>.全局修改或增加新属性 ''' class People(object): nationality = "CN" #类变量 def __init__(self,name,age,sex): self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。 self.age = age self.__sex = sex #定义私有属性,其只能在内部各函数(严格来说我们应该叫“内部方法”)中调用 def go_to_toilet(self): if self.__sex == "F": #在内部的方法中是可以成功调用的! print("stand up.....") else: print("蹲着.....") p = People("rianley","25","F") p.go_to_toilet() # print(p.sex) #外部调用的话会报错!因为私有属性只能内部方法调用,而不能从外部调用! #以上代码执行结果如下: stand up.....
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class People(object): nationality = "CN" #类变量 def __init__(self,name,age,sex): self.name = name #实例变量,换句话说只要在构造函数里面定义的变量都是实例变量!当然你也可以新增哟。 self.age = age self.__sex = sex #定义私有属性,其只能在内部各函数(严格来说我们应该叫“内部方法”)中调用 def get_sex(self): return self.__sex #将私有属性的值返回 p = People("rianley","25","F") print(p.get_sex()) #外部只能访问,不可以修改哟,(因为不能给方法复制!) #以上代码执行结果如下: F
七.继承
1.继承顺序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性 ''' 新式类继承顺序:F->D->B->E->C->A (广度优先,【不会优先找到最顶级的类】) 经典类继承顺序:F->D->B->A->E->C (深度优先) python3中统一都是新式类 pyhon2中才分新式类与经典类 ''' #以上代码执行结果如下: from D (<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__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
3.子类中调用父类方法
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法()
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class Vehicle: # 定义交通工具类 Country = 'China' def __init__(self, name, speed, load, power): self.name = name self.speed = speed self.load = load self.power = power def run(self): print('开动啦...') class Subway(Vehicle): # 地铁 def __init__(self, name, speed, load, power, line): Vehicle.__init__(self, name, speed, load, power) self.line = line def run(self): print('地铁%s号线欢迎您' % self.line) Vehicle.run(self) line15 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 15) line15.run() #以上代码执行结果如下: 地铁15号线欢迎您 开动啦...
方法二:super()
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class Vehicle: # 定义交通工具类 Country = 'China' def __init__(self, name, speed, load, power): self.name = name self.speed = speed self.load = load self.power = power def run(self): print('开动啦...') class Subway(Vehicle): # 地铁 def __init__(self, name, speed, load, power, line): # super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self) super().__init__(name, speed, load, power) self.line = line def run(self): print('地铁%s号线欢迎您' % self.line) super(Subway, self).run() class Mobike(Vehicle): # 摩拜单车 pass line13 = Subway('中国地铁', '180m/s', '1000人/箱', '电', 13) line13.run() #以上代码执行结果如下: 地铁13号线欢迎您 开动啦...
不用super引发的惨案
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com #每个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') A.__init__(self) class C(A): def __init__(self): print('C的构造方法') A.__init__(self) class D(B,C): def __init__(self): print('D的构造方法') B.__init__(self) C.__init__(self) pass f1=D() print(D.__mro__) #python2中没有这个属性 #以上代码执行结果如下: D的构造方法 B的构造方法 A的构造方法 C的构造方法 A的构造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com #每个类中都继承了且重写了父类的方法 class A: def __init__(self): print('A的构造方法') class B(A): def __init__(self): print('B的构造方法') super(B,self).__init__() class C(A): def __init__(self): print('C的构造方法') super(C,self).__init__() class D(B,C): def __init__(self): print('D的构造方法') super(D,self).__init__() f1=D() print(D.__mro__) #python2中没有这个属性 #以上代码执行结果如下: D的构造方法 B的构造方法 C的构造方法 A的构造方法 (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class SchoolMember(object): def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def tell(self): info = """ --------info of %s------------- name : %s age : %s sex : %s """%(self.name,self.name,self.age,self.sex) class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了. def __init__(self,name,age,sex,salary): #初始化函数 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量 self.salary = salary #表示重新构造函数 def teaching(self,course): print("%s is teaching %s"%(self.name,course)) t = Teacher("rianley",25,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。 print(t.name) print(t.sex) print(t.salary) #以上代码执行结果如下: rianley boy 1000
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class SchoolMember(object): members = 0 def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟! print("初始化了一个新学校成员",self.name) def tell(self): info = """ --------info of %s------------- name : %s age : %s sex : %s """%(self.name,self.name,self.age,self.sex) print(info) class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了. def __init__(self,name,age,sex,salary): #初始化函数 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量 self.salary = salary #表示重新构造函数 def teaching(self,course): print("%s is teaching %s"%(self.name,course)) class Student(SchoolMember): def __init__(self, name, age, sex, salary): # 初始化函数 SchoolMember.__init__(self, name, age, sex) def pay_tuition(self, amount): self.paid_tuition = amount print("student %s has paid tution amoint %s" % (self.name, amount)) t = Teacher("rianley",25,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。 s = Student("lifeng","26","M","pys600") t.tell() t.teaching("python") s.pay_tuition(11000) print("目前学校公有[%s]个学生"% SchoolMember.members) #以上代码执行结果如下: 初始化了一个新学校成员 rianley 初始化了一个新学校成员 lifeng --------info of yinzhengjie------------- name : rianley age : 25 sex : boy rianley is teaching python student lifeng has paid tution amoint 11000 目前学校公有[2]个学生
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com ''' 继承的作用: 1>.直接调用父类方法; 2>.继承父类方法并重构父类方法,先重构,在重构的方法里手动调用父类方法 3>.可以定义子类自己的方法; 4>.可以析构方法"__del__" ''' class SchoolMember(object): members = 0 def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟! print("初始化了一个新学校成员",self.name) def tell(self): info = """ --------info of %s------------- name : %s age : %s sex : %s """%(self.name,self.name,self.age,self.sex) print(info) def __del__(self): #析构函数(析构方法) print(("注意:[%s]已经被开除了!" % self.name)) SchoolMember.members -= 1 class Teacher(SchoolMember): #继承父类“SchoolMember”,这样的话如果父类的一些变量就不用重复再子类中定义了. def __init__(self,name,age,sex,salary): #初始化函数 SchoolMember.__init__(self,name,age,sex) #这个步骤表示将“name,age,sex”这几个值传递给父类SchoolMember中的"__init__(self,name,age,sex):",这样就继承了父类的所有实例变量 self.salary = salary #表示重新构造函数 def teaching(self,course): print("%s is teaching %s"%(self.name,course)) class Student(SchoolMember): def __init__(self, name, age, sex, salary): # 初始化函数 SchoolMember.__init__(self, name, age, sex) def pay_tuition(self, amount): self.paid_tuition = amount print("student %s has paid tution amoint %s" % (self.name, amount)) t = Teacher("rianley",21,"boy","1000") #调用子类的时候除了在父类中含有的功能外,还多了一个“salary”参数。 s = Student("lifeng","26","M","pys600") s2 = Student("test","22","F","pys16") s3 = Student("test_1","23","F","pys16") del s2 #删除一个实例用del,在实例被销毁后自动执行 t.tell() t.teaching("python") s.pay_tuition(11000) print("目前学校公有[%s]个学生"% SchoolMember.members) #以上大米执行结果如下: 初始化了一个新学校成员 rianley 初始化了一个新学校成员 lifeng 初始化了一个新学校成员 test 初始化了一个新学校成员 test_1 注意:[test]已经被开除了! --------info of yinzhengjie------------- name : yinzhengjie age : 25 sex : boy yinzhengjie is teaching python student lifeng has paid tution amoint 11000 目前学校公有[3]个学生 注意:[test_1]已经被开除了! #以为程序结束了,内存会被释放掉,所以在del实例后,会打印以下3行代码。 注意:[rianley]已经被开除了! 注意:[lifeng]已经被开除了!
八.组合
软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python课程。当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com class BirthDate: def __init__(self,year,month,day): self.year=year self.month=month self.day=day class Couse: def __init__(self,name,price,period): self.name=name self.price=price self.period=period class Teacher: def __init__(self,name,gender): self.name=name self.gender=gender def teach(self): print('teaching') class Professor(Teacher): def __init__(self,name,gender,birth,course): Teacher.__init__(self,name,gender) #直接将Teacher类的方法那来这个类里面用的方式就叫组合 self.birth=birth self.course=course p1=Professor('egon','male',BirthDate('1992','1','27'),Couse('python','28000','4 months')) print(p1.birth.year,p1.birth.month,p1.birth.day) print(p1.course.name,p1.course.price,p1.course.period) #以上代码执行结果如下: 1992 1 27 python 28000 4 months
九.接口与归一化设计
1.什么是接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
=================第一部分:Java 语言中的接口很好的展现了接口的含义: IAnimal.java /* * Java的Interface很好的体现了我们前面分析的接口的特征: * 1)是一组功能的集合,而不是一个功能 * 2)接口的功能用于交互,所有的功能都是public,即别的对象可操作 * 3)接口只定义函数,但不涉及函数实现 * 4)这些功能是相关的,都是动物相关的功能,但光合作用就不适宜放到IAnimal里面了 */ package com.oo.demo; public interface IAnimal { public void eat(); public void run(); public void sleep(); public void speak(); } =================第二部分:Pig.java:猪”的类设计,实现了IAnnimal接口 package com.oo.demo; public class Pig implements IAnimal{ //如下每个函数都需要详细实现 public void eat(){ System.out.println("Pig like to eat grass"); } public void run(){ System.out.println("Pig run: front legs, back legs"); } public void sleep(){ System.out.println("Pig sleep 16 hours every day"); } public void speak(){ System.out.println("Pig can not speak"); } } =================第三部分:Person2.java /* *实现了IAnimal的“人”,有几点说明一下: * 1)同样都实现了IAnimal的接口,但“人”和“猪”的实现不一样,为了避免太多代码导致影响阅读,这里的代码简化成一行,但输出的内容不一样,实际项目中同一接口的同一功能点,不同的类实现完全不一样 * 2)这里同样是“人”这个类,但和前面介绍类时给的类“Person”完全不一样,这是因为同样的逻辑概念,在不同的应用场景下,具备的属性和功能是完全不一样的 */ package com.oo.demo; public class Person2 implements IAnimal { public void eat(){ System.out.println("Person like to eat meat"); } public void run(){ System.out.println("Person run: left leg, right leg"); } public void sleep(){ System.out.println("Person sleep 8 hours every dat"); } public void speak(){ System.out.println("Hellow world, I am a person"); } } =================第四部分:Tester03.java package com.oo.demo; public class Tester03 { public static void main(String[] args) { System.out.println("===This is a person==="); IAnimal person = new Person2(); person.eat(); person.run(); person.sleep(); person.speak(); System.out.println("\n===This is a pig==="); IAnimal pig = new Pig(); pig.eat(); pig.run(); pig.sleep(); pig.speak(); } }
继承有两种用途:
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也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。
为什么要有抽象类:
如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。 比如我们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远无法吃到一个叫做水果的东西。从设计角度去看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。从实现角度来看,抽象类与普通类的不同之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点类似,但其实是不同的,即将揭晓答案
其实抽象类说直白了就是让你们的程序更加的规范化:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com #一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定义抽象方法,无需实现功能,只要这个类中的方法中含有该装饰器“abc.abstractmethod”,所以继承该类的人都要重写该方法! def read(self): '子类必须定义读功能' pass @abc.abstractmethod #定义抽象方法,无需实现功能 def write(self): '子类必须定义写功能' pass # class Txt(All_file): # pass # # t1=Txt() #报错,子类没有定义抽象方法 class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('文本数据的读取方法') def write(self): print('文本数据的读取方法') class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('硬盘数据的读取方法') def write(self): print('硬盘数据的读取方法') class Process(All_file): #子类继承抽象类,但是必须定义read和write方法 def read(self): print('进程数据的读取方法') def write(self): print('进程数据的读取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #这样大家都是被归一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type) #以上代码执行结果如下: 文本数据的读取方法 硬盘数据的读取方法 进程数据的读取方法 file file file
抽象类与接口
抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的相似性。抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计
十一.多态
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com #序列内形的不同形态如下:分别有字符串,元组和列表,这就体现了同一种食物的不同形态,简称多态。 name = "程小航" tuple_test = ("rianley","123") list_test = ["程小航","21","175","上海","黄埔"] #多态性:一个入口函数有多种实现方式 def fun(obj): #"obj"这个参数就体现多态性。原理就是传递进来的对象都有相同(同名)的“__len__()”方法,只不过同一个方法会执行不同的功能而已。 print(obj.__len__()) #相当于len(obj) fun(name) fun(tuple_test) fun(list_test) #以上代码的执行结果如下: 3 2 5
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com #多态如下: class Animal: def talk(self): pass class People(Animal): def talk(self): print("say hello") class Pig(Animal): def talk(self): print("say heng····heng") class Dog(Animal): def talk(self): print("say wang···wang") class Cat(Animal): def talk(self): print("miao~miao~miao~") p1 = People() #这就是一个实例,其本质上就是将“p1 = People()”传递给“self” pig1 = Pig() D1 = Dog() c = Cat() #多态性: def fun(obj): obj.talk() fun(p1) fun(pig1) fun(D1) fun(c) #以上代码执行结果如下: say hello say heng····heng say wang···wang miao~miao~miao~
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com #定义多态如下: class Animal(object): def __init__(self, name): # Constructor of the class self.name = name def talk(self): # Abstract method, defined by convention only raise NotImplementedError("Subclass must implement abstract method") class Cat(Animal): def talk(self): print('【%s】: 喵喵喵!' % self.name) class Dog(Animal): def talk(self): print('【%s】: 汪!汪!汪!' % self.name) #定义多态性: def func(obj): # 一个接口,多种形态 obj.talk() c1 = Cat('hello kitty') d1 = Dog('犬夜叉') func(c1) func(d1) #以上代码执行结果如下: 【hello kitty】: 喵喵喵! 【犬夜叉】: 汪!汪!汪!
Pyhon 很多语法都是支持多态的,比如 len(),sorted(), 你给len传字符串就返回字符串的长度,传列表就返回列表长度。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_coding:utf-8_*_ #@author :rianley #blog:http://www.cnblogs.com/rianley #EMAIL:rianley@qq.com ''' 继承的作用: 1>.直接调用父类方法; 2>.继承父类方法并重构父类方法,先重构,在重构的方法里手动调用父类方法 3>.可以定义子类自己的方法; 4>.可以析构方法"__del__" ''' class SchoolMember(object): members = 0 test = 111111 print("in SchoolMember ") def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex SchoolMember.members += 1 #注意着是调用的类变量,我们不能用"self.members"去自加哟! print("初始化了一个新学校成员",self.name) class Course(object): #定义一个类其实也可以不用谢初始化函数用,只写类变量也可以 test = 666666 print("in Course ") Course_name = "python自动化" period = "9m" outtline = "test" class Student(SchoolMember,Course): #继承了2个父类,继承顺序优先级由左往右依次递减 def __init__(self, name, age, sex, salary): # 初始化函数 SchoolMember.__init__(self, name, age, sex) def pay_tuition(self, amount): self.paid_tuition = amount #存在实例里,方便其他函数(方法)调用。 print("student %s has paid tution amoint %s" % (self.name, amount)) s = Student("rianley","25","M","pys11000") print(s.Course_name,Course.outtline) print(s.test) #以上代码执行结果如下: in SchoolMember in Course 初始化了一个新学校成员 rianley python自动化 test 111111
毒鸡汤:
其实,做一件事情,你智商不高没问题,你情商不高也没有问题,关键是在于坚持。欢迎加入程序员之家:687226766