Python从设计之初就已是一门面向对象的语言,正由于如此,在Python中建立一个类和对象是很容易的。本章节咱们将详细介绍Python的面向对象编程。java
若是你之前没有接触过面向对象的编程语言,那你可能须要先了解一些面向对象语言的一些基本特征,在头脑里头造成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。python
接下来咱们先来简单的了解下面向对象的一些基本特征。linux
和其它编程语言相比,Python 在尽量不增长新的语法和语义的状况下加入了类机制。git
Python中的类提供了面向对象编程的全部基本功能:类的继承机制容许多个基类,派生类能够覆盖基类中的任何方法,方法中能够调用基类中的同名方法。程序员
对象能够包含任意数量和类型的数据。github
语法格式以下:算法
class ClassName: <statement-1> . . . <statement-N>
类实例化后,可使用其属性,实际上,建立一个类以后,能够经过类名访问其属性。编程
类对象支持两种操做:属性引用和实例化。设计模式
属性引用使用和 Python 中全部的属性引用同样的标准语法:obj.name。微信
类对象建立后,类命名空间中全部的命名都是有效属性名。因此若是类定义是这样:
#!/usr/bin/python3 class MyClass: """一个简单的类实例""" i = 12345 def f(self): return 'hello world' # 实例化类 x = MyClass() # 访问类的属性和方法 print("MyClass 类的属性 i 为:", x.i) print("MyClass 类的方法 f 输出为:", x.f())
以上建立了一个新的类实例并将该对象赋给局部变量 x,x 为空的对象。
执行以上程序输出结果为:
MyClass 类的属性 i 为: 12345
MyClass 类的方法 f 输出为: hello world
不少类都倾向于将对象建立为有初始状态的。所以类可能会定义一个名为 __init__() 的特殊方法(构造方法),像下面这样:
def __init__(self): self.data = []
类定义了 __init__() 方法的话,类的实例化操做会自动调用 __init__() 方法。因此在下例中,能够这样建立一个新的实例:
x = MyClass()
固然, __init__() 方法能够有参数,参数经过 __init__() 传递到类的实例化操做上。例如:
实例(Python 3.0+) #!/usr/bin/python3 class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart x = Complex(3.0, -4.5) print(x.r, x.i) # 输出结果:3.0 -4.5
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt() #以上实例执行结果为: <__main__.Test instance at 0x100771878> __main__.Test
从执行结果能够很明显的看出,self 表明的是类的实例,表明当前对象的地址,而 self.class 则指向类。
self 不是 python 关键字,咱们把他换成 runoob 也是能够正常执行的:
class Test: def prt(runoob): print(runoob) print(runoob.__class__) t = Test() t.prt() #以上实例执行结果为: <__main__.Test instance at 0x100771878> __main__.Test
在类地内部,使用 def 关键字来定义一个方法,与通常函数定义不一样,类方法必须包含参数 self, 且为第一个参数,self 表明的是类的实例。
实例(Python 3.0+) #!/usr/bin/python3 #类定义 class people: #定义基本属性 name = '' age = 0 #定义私有属性,私有属性在类外部没法直接进行访问 __weight = 0 #定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 说: 我 %d 岁。" %(self.name,self.age)) # 实例化类 p = people('runoob',10,30) p.speak() #执行以上程序输出结果为: runoob 说: 我 10 岁。
在类地内部,使用 def 关键字来定义一个方法,与通常函数定义不一样,类方法必须包含参数 self,且为第一个参数,self 表明的是类的实例。
self 的名字并非规定死的,也可使用 this,可是最好仍是按照约定是用 self。
面向过程的程序设计的核心是过程(流水线式思惟),过程即解决问题的步骤,面向过程的设计就比如精心设计好一条流水线,考虑周全何时处理什么东西。
优势是:极大的下降了写程序的复杂度,只须要顺着要执行的步骤,堆叠代码便可。
缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
用场景:一旦完成基本不多改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向对象的程序设计的核心是对象(上帝式思惟),要理解对象为什么物,必须把本身当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也能够创造出来。面向对象的程序设计比如如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题须要四我的:唐僧,沙和尚,猪八戒,孙悟空,每一个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并很差玩,因而如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。而后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。
面向对象的程序设计的
优势是:解决了程序的扩展性。对某一个对象单独修改,会马上反映到整个体系中,如对游戏中一我的物参数的特征和技能修改都很容易。
缺点:可控性差,没法向面向过程的程序设计流水线式的能够很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即使是上帝也没法预测最终结果。因而咱们常常看到一个游戏人某一参数的修改极有可能致使阴霸的技能出现,一刀砍死3我的,这个游戏就失去平衡。
应用场景:需求常常变化的软件,通常需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
在python 中面向对象的程序设计并非所有。
面向对象编程可使程序的维护和扩展变得更简单,而且能够大大提升程序开发效率 ,另外,基于面向对象的程序可使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
了解一些名词:类、对象、实例、实例化
类:具备相同特征的一类事物(人、狗、老虎)
对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)
实例化:类——>对象的过程(这在生活中表现的不明显,咱们在后面再慢慢解释)
定义一我的类
class Person: # 定义一我的类 role = 'person' # 人的角色属性都是人 def __init__(self, name, aggressivity, life_value, money): self.name = name # 每个角色都有本身的昵称; self.aggressivity = aggressivity # 每个角色都有本身的攻击力; self.life_value = life_value # 每个角色都有本身的生命值; self.money = money def attack(self,dog): # 人能够攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而降低 dog.life_value -= self.aggressivity
定义一个狗类
class Dog: # 定义一个狗类 role = 'dog' # 狗的角色属性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一只狗都有本身的昵称; self.breed = breed # 每一只狗都有本身的品种; self.aggressivity = aggressivity # 每一只狗都有本身的攻击力; self.life_value = life_value # 每一只狗都有本身的生命值; def bite(self,people): # 狗能够咬人,这里的狗也是一个对象。 # 狗咬人,那么人的生命值就会根据狗的攻击力而降低 people.life_value -= self.aggressivity
接下来,又建立一个新的兵器类。
class Weapon: def __init__(self,name, price, aggrev, life_value): self.name = name self.price = price self.aggrev = aggrev self.life_value = life_value def update(self, obj): #obj就是要带这个装备的人 obj.money -= self.price # 用这个武器的人花钱买因此对应的钱要减小 obj.aggressivity += self.aggrev # 带上这个装备可让人增长攻击 obj.life_value += self.life_value # 带上这个装备可让人增长生命值 def prick(self, obj): # 这是该装备的主动技能,扎死对方 obj.life_value -= 500 # 假设攻击力是500
测试交互
lance = Weapon('长矛',200,6,100) egg = Person('egon',10,1000,600) #创造了一个实实在在的人egg ha2 = Dog('二愣子','哈士奇',10,1000) #创造了一只实实在在的狗ha2 #egg独自力战"二愣子"深感吃力,决定穷毕生积蓄买一把武器 if egg.money > lance.price: #若是egg的钱比装备的价格多,能够买一把长矛 lance.update(egg) #egg花钱买了一个长矛防身,且自身属性获得了提升 egg.weapon = lance #egg装备上了长矛 print(egg.money,egg.life_value,egg.aggressivity) print(ha2.life_value) egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) egg.weapon.prick(ha2) #发动武器技能 print(ha2.life_value) #ha2不敌狡猾的人类用武器取胜,血槽空了一半
按照这种思路一点一点的设计类和对象,最终你彻底能够实现一个对战类游戏。
建立一个类就会建立一个类的名称空间,用来存储类中定义的全部名字,这些名字称为类的属性
而类有两种属性:静态属性和动态属性
>>>id(egg.role) 4341594072 >>>id(Person.role) 4341594072
>>>egg.attack <bound method Person.attack of <__main__.Person object at 0x101285860>> >>>Person.attack <function Person.attack at 0x10127abf8>
建立一个对象/实例就会建立一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
在obj.name会先从obj本身的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
软件重用的重要方式除了继承以外还有另一种方式,即:组合
组合指的是,在一个类中以另一个类的对象做为数据属性,称为类的组合
class Weapon: def prick(self, obj): # 这是该装备的主动技能,扎死对方 obj.life_value -= 500 # 假设攻击力是500 class Person: # 定义一我的类 role = 'person' # 人的角色属性都是人 def __init__(self, name): self.name = name # 每个角色都有本身的昵称; self.weapon = Weapon() # 给角色绑定一个武器; egg = Person('egon') egg.weapon.prick() #egg组合了一个武器的对象,能够直接egg.weapon来使用组合类中的全部方法
圆环是由两个圆组成的,圆环的面积是外面圆的面积减去内部圆的面积。圆环的周长是内部圆的周长加上外部圆的周长。
这个时候,咱们就首先实现一个圆形类,计算一个圆的周长和面积。而后在"环形类"中组合圆形的实例做为本身的属性来用
from math import pi class Circle: ''' 定义了一个圆形类; 提供计算面积(area)和周长(perimeter)的方法 ''' def __init__(self,radius): self.radius = radius def area(self): return pi * self.radius * self.radius def perimeter(self): return 2 * pi *self.radius circle = Circle(10) #实例化一个圆 area1 = circle.area() #计算圆面积 per1 = circle.perimeter() #计算圆周长 print(area1,per1) #打印圆面积和周长 class Ring: ''' 定义了一个圆环类 提供圆环的面积和周长的方法 ''' def __init__(self,radius_outside,radius_inside): self.outsid_circle = Circle(radius_outside) self.inside_circle = Circle(radius_inside) def area(self): return self.outsid_circle.area() - self.inside_circle.area() def perimeter(self): return self.outsid_circle.perimeter() + self.inside_circle.perimeter() ring = Ring(10,5) #实例化一个环形 print(ring.perimeter()) #计算环形的周长 print(ring.area()) #计算环形的面积
用组合的方式创建了类与组合的类之间的关系,它是一种‘有’的关系,好比教授有生日,教授教python课程
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,birth,course): self.name=name self.gender=gender self.birth=birth self.course=course def teach(self): print('teaching') p1=Teacher('egon','male', BirthDate('1995','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) ''' 运行结果: 27 python 28000 4 months '''
当类之间有显著不一样,而且较小的类是较大的类所须要的组件时,用组合比较好
class Person: # 定义一我的类 role = 'person' # 人的角色属性都是人 def __init__(self, name, aggressivity, life_value, money): self.name = name # 每个角色都有本身的昵称; self.aggressivity = aggressivity # 每个角色都有本身的攻击力; self.life_value = life_value # 每个角色都有本身的生命值; self.money = money def attack(self,dog): # 人能够攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而降低 dog.life_value -= self.aggressivity class Person: # 定义一我的类 role = 'person' # 人的角色属性都是人 def __init__(self, name, aggressivity, life_value, money): self.name = name # 每个角色都有本身的昵称; self.aggressivity = aggressivity # 每个角色都有本身的攻击力; self.life_value = life_value # 每个角色都有本身的生命值; self.money = money def attack(self,dog): # 人能够攻击狗,这里的狗也是一个对象。 # 人攻击狗,那么狗的生命值就会根据人的攻击力而降低 dog.life_value -= self.aggressivity class Weapon: def __init__(self,name, price, aggrev, life_value): self.name = name self.price = price self.aggrev = aggrev self.life_value = life_value def update(self, obj): #obj就是要带这个装备的人 obj.money -= self.price # 用这个武器的人花钱买因此对应的钱要减小 obj.aggressivity += self.aggrev # 带上这个装备可让人增长攻击 obj.life_value += self.life_value # 带上这个装备可让人增长生命值 def prick(self, obj): # 这是该装备的主动技能,扎死对方 obj.life_value -= 500 # 假设攻击力是500 lance = Weapon('长矛',200,6,100) egg = Person('egon',10,1000,600) #创造了一个实实在在的人egg ha2 = Dog('二愣子','哈士奇',10,1000) #创造了一只实实在在的狗ha2 #egg独自力战"二愣子"深感吃力,决定穷毕生积蓄买一把武器 if egg.money > lance.price: #若是egg的钱比装备的价格多,能够买一把长矛 lance.update(egg) #egg花钱买了一个长矛防身,且自身属性获得了提升 egg.weapon = lance #egg装备上了长矛 print(egg.money,egg.life_value,egg.aggressivity) print(ha2.life_value) egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) egg.weapon.prick(ha2) #发动武器技能 print(ha2.life_value) #ha2不敌狡猾的人类用武器取胜,血槽空了一半
继承是一种建立新类的方式,在python中,新建的类能够继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类 pass class ParentClass2: #定义父类 pass class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类 pass
查看继承
>>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看全部继承的父类 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
提示:若是没有指定基类,python的类会默认继承object类,object是全部python类的基类,它提供了一些常见方法(如__str__)的实现。
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
抽象即抽取相似或者说比较像的部分。
抽象分红两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的做用是划分类别(能够隔离关注点,下降复杂度)
继承:是基于抽象的结果,经过编程语言去实现它,确定是先经历抽象这个过程,才能经过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程当中,一个动做或者说一种技巧,经过抽象能够获得类
==========================第一部分 例如 猫能够:喵喵叫、吃、喝、拉、撒 狗能够:汪汪叫、吃、喝、拉、撒 若是咱们要分别为猫和狗建立一个类,那么就须要为 猫 和 狗 实现他们全部的功能,伪代码以下: #猫和狗有大量相同的内容 class 猫: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '汪汪叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something ==========================第二部分 上述代码不难看出,吃、喝、拉、撒是猫和狗都具备的功能,而咱们却分别的猫和狗的类中编写了两次。若是使用 继承 的思想,以下实现: 动物:吃、喝、拉、撒 猫:喵喵叫(猫继承动物的功能) 狗:汪汪叫(狗继承动物的功能) 伪代码以下: class 动物: def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something # 在类后面括号中写入另一个类名,表示当前类继承另一个类 class 猫(动物): def 喵喵叫(self): print '喵喵叫' # 在类后面括号中写入另一个类名,表示当前类继承另一个类 class 狗(动物): def 汪汪叫(self): print '汪汪叫' ==========================第三部分 #继承的代码实现 class Animal: def eat(self): print("%s 吃 " %self.name) def drink(self): print ("%s 喝 " %self.name) def shit(self): print ("%s 拉 " %self.name) def pee(self): print ("%s 撒 " %self.name) class Cat(Animal): def __init__(self, name): self.name = name self.breed = '猫' def cry(self): print('喵喵叫') class Dog(Animal): def __init__(self, name): self.name = name self.breed='狗' def cry(self): print('汪汪叫') # ######### 执行 ######### c1 = Cat('小白家的小黑猫') c1.eat() c2 = Cat('小黑的小白猫') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat() 使用继承来重用代码比较好的例子
在开发程序的过程当中,若是咱们定义了一个类A,而后又想新创建另一个类B,可是类B的大部份内容与类A的相同时
咱们不可能从头开始写一个类B,这就用到了类的继承的概念。
经过继承的方式新建类B,让B继承A,B会‘遗传’A的全部属性(数据属性和函数属性),实现代码重用
class Animal: ''' 人和狗都是动物,因此创造一个Animal基类 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有本身的昵称; self.aggressivity = aggressivity # 人和狗都有本身的攻击力; self.life_value = life_value # 人和狗都有本身的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): pass class Person(Animal): pass egg = Person('egon',10,1000) ha2 = Dog('二愣子',50,1000) egg.eat() ha2.eat()
提示:用已经有的类创建一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工做量,这就是常说的软件重用,不只能够重用本身的类,也能够继承别人的,好比标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来讲,意义重大.
固然子类也能够添加本身新的属性或者在本身这里从新定义这些属性(不会影响到父类),须要注意的是,一旦从新定义了本身的属性且与父类重名,那么调用新增的属性时,就以本身为准了。
class Animal: ''' 人和狗都是动物,因此创造一个Animal基类 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有本身的昵称; self.aggressivity = aggressivity # 人和狗都有本身的攻击力; self.life_value = life_value # 人和狗都有本身的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): ''' 狗类,继承Animal类 ''' def bite(self, people): ''' 派生:狗有咬人的技能 :param people: ''' people.life_value -= self.aggressivity class Person(Animal): ''' 人类,继承Animal ''' def attack(self, dog): ''' 派生:人有攻击的技能 :param dog: ''' dog.life_value -= self.aggressivity egg = Person('egon',10,1000) ha2 = Dog('二愣子',50,1000) print(ha2.life_value) print(egg.attack(ha2)) print(ha2.life_value)
注意:像ha2.life_value之类的属性引用,会先从实例中找life_value而后去类中找,而后再去父类中找...直到最顶级的父类。
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能须要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,所以即使是self参数也要为其传值.
在python3中,子类执行父类的方法也能够直接用super方法.
class Animal: ''' 人和狗都是动物,因此创造一个Animal基类 ''' def __init__(self, name, aggressivity, life_value): self.name = name # 人和狗都有本身的昵称; self.aggressivity = aggressivity # 人和狗都有本身的攻击力; self.life_value = life_value # 人和狗都有本身的生命值; def eat(self): print('%s is eating'%self.name) class Dog(Animal): ''' 狗类,继承Animal类 ''' def __init__(self,name,breed,aggressivity,life_value): super().__init__(name, aggressivity, life_value) #执行父类Animal的init方法 self.breed = breed #派生出了新的属性 def bite(self, people): ''' 派生出了新的技能:狗有咬人的技能 :param people: ''' people.life_value -= self.aggressivity def eat(self): # Animal.eat(self) #super().eat() print('from Dog') class Person(Animal): ''' 人类,继承Animal ''' def __init__(self,name,aggressivity, life_value,money): #Animal.__init__(self, name, aggressivity, life_value) #super(Person, self).__init__(name, aggressivity, life_value) super().__init__(name,aggressivity, life_value) #执行父类的init方法 self.money = money #派生出了新的属性 def attack(self, dog): ''' 派生出了新的技能:人有攻击的技能 :param dog: ''' dog.life_value -= self.aggressivity def eat(self): #super().eat() Animal.eat(self) print('from Person') egg = Person('egon',10,1000,600) ha2 = Dog('二愣子','哈士奇',10,1000) print(egg.name) print(ha2.name) egg.eat()
经过继承创建了派生类与基类之间的关系,它是一种'是'的关系,好比白马是马,人是动物。
当类之间有不少相同的功能,提取这些共同的功能作成基类,用继承比较好,好比教授是老师
>>> class Teacher: ... def __init__(self,name,gender): ... self.name=name ... self.gender=gender ... def teach(self): ... print('teaching') ... >>> >>> class Professor(Teacher): ... pass ... >>> p1=Professor('egon','male') >>> p1.teach() teaching
继承有两种用途:
一:继承基类的方法,而且作出本身的改变或者扩展(代码重用)
二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,而且实现接口中的功能
class Alipay: ''' 支付宝支付 ''' def pay(self,money): print('支付宝支付了%s元'%money) class Applepay: ''' apple pay支付 ''' def pay(self,money): print('apple pay支付了%s元'%money) def pay(payment,money): ''' 支付函数,整体负责支付 对应支付的对象和要支付的金额 ''' payment.pay(money) p = Alipay() pay(p,200) #。。。。。。。。。。。 class Alipay: ''' 支付宝支付 ''' def pay(self,money): print('支付宝支付了%s元'%money) class Applepay: ''' apple pay支付 ''' def pay(self,money): print('apple pay支付了%s元'%money) class Wechatpay: def fuqian(self,money): ''' 实现了pay的功能,可是名字不同 ''' print('微信支付了%s元'%money) def pay(payment,money): ''' 支付函数,整体负责支付 对应支付的对象和要支付的金额 ''' payment.pay(money) p = Wechatpay() pay(p,200) #执行会报错 class Payment: def pay(self): raise NotImplementedError class Wechatpay(Payment): def fuqian(self,money): print('微信支付了%s元'%money) p = Wechatpay() #这里不报错 pay(p,200) #这里报错了 #。。。。。。。。。。。。。 from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def pay(self,money): pass class Wechatpay(Payment): def fuqian(self,money): print('微信支付了%s元'%money) p = Wechatpay() #不调就报错了
class Alipay: ''' 支付宝支付 ''' def pay(self,money): print('支付宝支付了%s元'%money) class Applepay: ''' apple pay支付 ''' def pay(self,money): print('apple pay支付了%s元'%money) class Wechatpay: def fuqian(self,money): ''' 实现了pay的功能,可是名字不同 ''' print('微信支付了%s元'%money) def pay(payment,money): ''' 支付函数,整体负责支付 对应支付的对象和要支付的金额 ''' payment.pay(money) p = Wechatpay() pay(p,200) #执行会报错
接口初成:手动报异常:NotImplementedError来解决开发中遇到的问题
class Payment: def pay(self): raise NotImplementedError class Wechatpay(Payment): def fuqian(self,money): print('微信支付了%s元'%money) p = Wechatpay() #这里不报错 pay(p,200) #这里报错了
借用abc模块来实现接口
from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def pay(self,money): pass class Wechatpay(Payment): def fuqian(self,money): print('微信支付了%s元'%money) p = Wechatpay() #不调就报错了
实践中,继承的第一种含义意义并不很大,甚至经常是有害的。由于它使得子类与基类出现强耦合。
继承的第二种含义很是重要。它又叫“接口继承”。
接口继承实质上是要求“作出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的全部对象”——这在程序设计上,叫作归一化。
归一化使得高层的外部使用者能够不加区分的处理全部接口兼容的对象集合——就好象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
接口提取了一群类共同的函数,能够把接口当作一个函数的集合。
而后让子类去实现接口中的函数。
这么作的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么全部的这些类产生的对象在使用时,从用法上来讲都同样。
归一化,让使用者无需关心对象的类是什么,只须要的知道这些对象都具有某些功能就能够了,这极大地下降了使用者的使用难度。
好比:咱们定义一个动物接口,接口里定义了有跑、吃、呼吸等接口函数,这样老鼠的类去实现了该接口,松鼠的类也去实现了该接口,由两者分别产生一只老鼠和一只松鼠送到你面前,即使是你分别不到底哪只是什么鼠你确定知道他俩都会跑,都会吃,都能呼吸。
再好比:咱们有一个汽车接口,里面定义了汽车全部的功能,而后由本田汽车的类,奥迪汽车的类,大众汽车的类,他们都实现了汽车接口,这样就好办了,你们只须要学会了怎么开汽车,那么不管是本田,仍是奥迪,仍是大众咱们都会开了,开的时候根本无需关心我开的是哪一类车,操做手法(函数调用)都同样
为什么要用接口
什么是抽象类
与java同样,python也有抽象类的概念可是一样须要借助模块实现,抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化
为何要有抽象类
若是说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
好比咱们有香蕉的类,有苹果的类,有桃子的类,从这些类抽取相同的内容就是水果这个抽象的类,你吃水果时,要么是吃一个具体的香蕉,要么是吃一个具体的桃子。。。。。。你永远没法吃到一个叫作水果的东西。
从设计角度去看,若是类是从现实对象抽象而来的,那么抽象类就是基于类抽象而来的。
从实现角度来看,抽象类与普通类的不一样之处在于:抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。这一点与接口有点相似,但实际上是不一样的,即将揭晓答案
在python中实现抽象类
#一切皆文件 import abc #利用abc模块实现抽象类 class All_file(metaclass=abc.ABCMeta): all_type='file' @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)
抽象类的本质仍是类,指的是一组类的类似性,包括数据属性(如all_type)和函数属性(如read、write),而接口只强调函数属性的类似性。
抽象类是一个介于类和接口直接的一个概念,同时具有类和接口的部分特性,能够用来实现归一化设计
在python中,并无接口类这种东西,即使不经过专门的模块定义接口,咱们也应该有一些基本的概念。
在继承抽象类的过程当中,咱们应该尽可能避免多继承;
而在继承接口的时候,咱们反而鼓励你来多继承接口
接口隔离原则: 使用多个专门的接口,而不使用单一的总接口。即客户端不该该依赖那些不须要的接口。
2.方法的实现
在抽象类中,咱们能够对一些抽象方法作出基础实现;
而在接口类中,任何方法都只是一种规范,具体的功能须要子类实现
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中才分新式类与经典类 继承顺序
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.若是对下一个类存在两个合法的选择,选择第一个父类
继承的做用:
减小代码的重用
提升代码可读性
规范编程模式
几个名词:
抽象:抽象即抽取相似或者说比较像的部分。是一个从具题到抽象的过程。
继承:子类继承了父类的方法和属性
派生:子类在父类方法和属性的基础上产生了新的方法和属性
#egon老师的生日 是否是birth类的一个实例 #egon.birth = Birth() #组合 #在继承中 #继承的语法: # class 类名(父类名): # 想在子类中实现调用父类的方法 #在类内 ——super(子类名,self).方法名() #在类外面 ——super(子类名,对象名).方法名() #若是不指定继承的父类,默认继承object #子类可使用父类的全部属性和方法 #若是子类有本身的方法就执行本身的的 #若是是子类没有的方法就执行父类的 #若是子类父类都没有这个方法就报错 #继承、抽象、派生 #继承 是从大范围到小范围 #抽象 小范围到大范围 #派生 就是在父类的基础上又产生子类——派生类 #父类里没有的 但子类有的 ——派生方法 #派生属性 #方法的重写 #父类里有的方法,在子类里从新实现
1.多继承问题 在继承抽象类的过程当中,咱们应该尽可能避免多继承; 而在继承接口的时候,咱们反而鼓励你来多继承接口 2.方法的实现 在抽象类中,咱们能够对一些抽象方法作出基础实现; 而在接口类中,任何方法都只是一种规范,具体的功能须要子类实现
新式类:广度优先 经典类:深度优先
多态指的是一类事物有多种形态
动物有多种形态:人,狗,猪
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之二:狗 def talk(self): print('say wangwang') class Pig(Animal): #动物的形态之三:猪 def talk(self): print('say aoao')
文件有多种形态:文本文件,可执行文件
import abc class File(metaclass=abc.ABCMeta): #同一类事物:文件 @abc.abstractmethod def click(self): pass class Text(File): #文件的形态之一:文本文件 def click(self): print('open file') class ExeFile(File): #文件的形态之二:可执行文件 def click(self): print('execute file')
一 什么是多态动态绑定(在继承的背景下使用时,有时也称为多态性)
多态性是指在不考虑实例类型的状况下使用实例
在面向对象方法中通常是这样表述多态性:
向不一样的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不一样的对象在接收时会产生不一样的行为(即方法)。
也就是说,每一个对象能够用本身的方式去响应共同的消息。所谓消息,就是调用函数,不一样的行为就是指不一样的实现,即执行不一样的函数。
好比:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操做,学生执行的是放学操做,虽然两者消息同样,可是执行的效果不一样
多态性分为静态多态性和动态多态性
静态多态性:如任何类型均可以用运算符+进行运算
动态多态性:以下
peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物确定有talk方法 #因而咱们能够不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,咱们能够定义一个统一的接口来使用 def func(obj): obj.talk()
鸭子类型
逗比时刻:
Python崇尚鸭子类型,即‘若是看起来像、叫声像并且走起路来像鸭子,那么它就是鸭子’
python程序员一般根据这种行为来编写程序。例如,若是想编写现有对象的自定义版本,能够继承该对象
也能够建立一个外观和行为像,但与它无任何关系的全新对象,后者一般用于保存程序组件的松耦合度。
例1:利用标准库中定义的各类‘与文件相似’的对象,尽管这些对象的工做方式像文件,但他们没有继承内置文件对象的方法
例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系
#两者都像鸭子,两者看起来都像文件,于是就能够当文件同样去用 class TxtFile: def read(self): pass def write(self): pass class DiskFile: def read(self): pass def write(self): pass 例子