目录python
继承是面向对象的重要特性之一,是相对两个类而言的父子关系,子类继承了父类的全部的属性和方法,继承最大的好处是实现了代码的重用,能够重用已经存在的数据和行为,减小代码的重复编写。算法
在Python2.2以前,类是没有共同的祖先的,以后,引入了object类,它是全部类的共同祖先类。Python2中为了兼容,分为古典类(旧式类)和新式类。而在Python 3中所有都为新式类,新式类都是继承object类的,而且可使用super函数(后面会说)。下面是Python2.x中的代码设计模式
class A: pass class B(object): pass >>> dir(A) # 查看类的__dict__ ['__doc__', '__module__'] >>> dir(B) ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
在Python2.x中 A和B是不一样的两个类。A没有继承,被称为古典类,B继承自object,被称为新式类。不止少了不少方法,连实例对象的属性也是不太相同的。Python 3中的代码以下ssh
class A: pass class B(object): pass >>> dir(A) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] >>> dir(B) ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__'] >>>
在Python 3中,都为新式类,因此A和A(object)是两个结果相同的不一样写法而已 class A:pass == class A(object):pass
。ide
更多的区别这里就不在详述,Python 3是将来,忘记旧式类吧。函数
Python在类名后使用一对括号来表示继承关系,括号中的类即为父类。先来看看不用继承的例子:优化
class Animal: def shout(self): print('{} shout'.format(self.__class__.__name__)) class Cat: def shout(self): print('{} shout'.format(self.__class__.__name__)) class Dog: def shout(self): print('{} shout'.format(self.__class__.__name__)) a = Animal() c = Cat() d = Dog() a.shout() c.shout() d.shout()
从上面例子来看,虽然猫狗动物均可以叫,可是却分别实现了叫这个动做,那么下一步使用继承来优化.net
class Animal: def shout(self): print('{} shout'.format(self.__class__.__name__)) class Cat(Animal): pass class Dog(Animal): pass a = Animal() c = Cat() d = Dog() a.shout() c.shout() d.shout()
经过继承,猫类、狗类就不用写代码,直接继承了父类Animal类的叫方法了。因此,在上面的例子中:设计
和继承相关的经常使用特殊属性和方法以下:code
特殊属性和方法 | 含义 | 示例 |
---|---|---|
__base__ |
类的基类 | |
__bases__ |
类的基类们(元组) | |
__mro__ |
方法解析顺序(基类们的元组) | |
mro() |
方法解析顺序(基类们的列表) | int.mro() |
__subclasses__() |
类的子类列表 | int.__subclasses__() |
经过一个例子来看继承中的访问控制
class Animal: __COUNT = 100 # _Animal__COUNT = 100 HEIGHT = 0 def __init__(self, age,weight,height): self.__COUNT += 1 # self._Animal_COUNT = self._Animal_COUNT + 1 self.age = age self.__weight = weight # self._Animal__weight = weight self.HEIGHT = height def eat(self): print('{} eat'.format(self.__class__.__name__)) def __getweight(self): # def _Animal__getweight(self): print(self.__weight) # print(self._Animal__weight) @classmethod def showcount1(cls): print(cls) print(cls.__dict__) print(cls.__COUNT) # print(cls._Animal__COUNT) @classmethod def __showcount2(cls): # def _Animal__showcount2(cls): print(cls.__COUNT) # print(cls._Animal__COUNT) def showcount3(self): print(self.__COUNT) # print(self._Animal__COUNT) class Cat(Animal): NAME = 'CAT' __COUNT = 200 # _Cat__Count = 200 c = Cat(3,5,15) c.eat() # 1 print(c.HEIGHT) # 2 print(c.__COUNT) # 3 print('c:',c.__dict__) print('cat:',Cat.__dict__) print('Animal:',Animal.__dict__) c.showcount1() # 4 c.showcount2() # 5 c.showcount3() # 6 print(c.NAME) # 7
分析:
'Cat eat'
。15
。没有该属性
。Cat类
,cls.__dict__就是Cat.__dict__
,而Cat中只定义两个属性,因此值为{'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
(魔术方法先不考虑),类方法定义在哪一个类中,那么私有变量就会被改为以那个类为前缀的变量名,因此showcount1方法定义在Animal中,__COUNT就变成了_Animal__COUNT。cls.__COUNT就是在寻找cls._Animal__COUNT属性,而Cat类中的__COUNT被更名为_Cat__COUNT,因此最后只能在Animal中找到,因此值为100
。没有该属性方法
。showcount3定义在Animal中,因此self.__COUNT实际上为self._Animal__COUNT,c在实例化的同时进行了初始化,Cat没了__init__函数,因此继承了父类Animal的init,在初始化过程当中定义的self.__COUNT,实际上就是self._Animal__COUNT,这里self._Animal__COUNT = self._Animal__COUNT + 1,先算等式右边,在执行时c尚未_Animal__COUNT属性,因此会从Cat类开始找直到Animal类,Cat类的__COUNT更名为了_Cat__COUNT,不是咱们想要的,而后找到了Animal的100,而后加1,再赋给实例c(等于c._Animal__COUNT = Animal._Animal__COUNT + 1),因此实例c来讲,它本身就已经拥有_Animal__COUNT属性,它的值为101
。
通常状况下不会这么写,这里只是练习知识点。分析这种状况时,直接把私有变量/方法更名后就很是好分析了。
总结:
实例属性查找顺序:实例的__dict__ , 类的__dict__ , 父类的__dict__(若是有继承)。
遇到私有变量/方法看定义的位置,直接进行更名就比较好分析了。
方法重写,顾名思义就是重写继承来的方法,Python和其余语言不一样的是,在Java中,要重写的方法,参数数量和类型要和原方法彻底相同才行,不然会被认为是方法重载。Python因为其动态的语言的特性,只要方法相同,则表示的就是方法重写。
class Animal: def __init__(self, name): self.name = name def shout(self): print('Animal shout') class Cat(Animal): def shout(self): print('miaomiao') c = Cat('daxin') c.shout()
上面的例子中,Cat继承了Animal类的shout方法,可是因为Animal中定义的shout不符合Cat的需求,因此在Cat中重写了shout方法,可是须要注意的是,重写不是覆盖,严格来讲的话应该算是遮盖,由于Python的类查找顺序是按照当前实例->父类->基类等来的,因此当给Cat类定义shout方法后,实例调用方法shout时,父类中已经包含了shout方法,因此就直接调用了,而Animal中的shout依旧在,只是Cat的shout方法被预先发现了。
若是咱们并非要改写,而是要加强原方法的功能呢?
class Animal: def shout(self): print('Animal shout') class Cat(Animal): def shout(self): self.__class__.__base__.shout(self) # 须要手动传入self print('miaomiao') c = Cat() c.shout()
经过查找父类而后传入实例调用,是能够的,可是不建议这样使用,在这种状况下,咱们通常会使用super
.
super
函数(类)是用于调用父类(超类)的一个方法的。主要的两种写法以下:
super() # 指代父类 super(type, obj) # 一样指代父类,super接受两个参数,第一个是类型,第二个是实例对象。 super() == super(type, obj)
super(type, obj) 通常写为super(self.__class__, self) 按照上面Animal的例子的话,就为super(Cat, self)
利用super完成加强方法的例子:
class Animal: def shout(self): print('Animal shout') class Cat(Animal): def shout(self): super(Cat, self).shout() # super().shout() print('miaomiao') c = Cat() c.shout()
静态方法和类方法均可以被覆盖,原理都相同,都是在属性字典__dict__中搜索。
class Animal: def __init__(self, name): self.name = name def shout(self): print('Animal shout') class Cat(Animal): def __init__(self, age): self.age = age def shout(self): print('miaomiao') c = Cat('daxin',20) c.shout() print(c.name)
请问这种状况是能够执行吗?答案是不行的,由于Cat重写了__init__方法,因此在c实例化时,只能访问Cat类的__init__方法,因此,就须要显示的调用父类的__init__方法。
class Animal: def __init__(self, name): self.name = name def shout(self): print('Animal shout') class Cat(Animal): def __init__(self, name, age): super(Cat, self).__init__(name) # 等于把实例和name变量,传递给Animal.__init__(self,name) # super().__init__(name) # 效果相同 self.age = age def shout(self): print('miaomiao') c = Cat('daxin',20) c.shout()
须要注意的是,若是有同名属性,那么后执行的会覆盖先执行的。
def __init__(self, name, age): super(Cat, self).__init__(name) self.name = age # 覆盖父类初始化的name属性
另外在私有属性继承的状况下,请注意真正的变量名称(由于会更名)。
一个类继承自多个类就是多继承,它将具备多个类的特征。面向对象的设计的开闭原则(实体应该对扩展开放,而对修改封闭),就能够利用继承来设计,即多用'继承',少修改(并非通常的多继承,后面会详述)。它的定义个数以下
class MyClass(A,B,...):pass
Python中的继承关系,分为多继承和单继承,如图所示:
多继承很好的模拟了世界,由于事物不多是单一继承,可是舍弃简单,必然引入复杂性,带来了冲突。举个简单的例子:若是一个孩子继承了来自父母双方的特称,那么到底眼睛像爸爸仍是妈妈呢,孩子究竟像谁多一点呢?
多继承的实现或致使编译器设计的复杂度增长,因此如今不少语言也舍弃了类的多继承。
C++支持多继承,而Java舍弃了多继承。有些人说Java支持的多继承的,其实他说的是接口,在Java中,一个类能够实现多个接口,一个接口也能够继承多个接口。Java的接口很纯粹,只是方法的生命,继承者必须实现这些方法,就具备了这些能力,就能干什么。
多继承带来的问题,最主要的就是二义性,例如猫和狗都继承自动物类,如今若是一个类继承了猫和狗类,猫和狗都有shout方法,子类究竟继承谁的shout呢?
实现多继承的语言,要解决二义性,主要有两种方法
深度优先
和广度优先
。
MRO:方法解析顺序,Python使用MRO来解决多继承带来的二义性问题。由于Python 2.x的旧式类和新式类等历史缘由(旧式类不会继承object对想),MRO有三个搜索算法:
MyClass、D、B、A、C
MyClass、D、B、C、A、object
MyClass、D、B、C、A、object
经典算法有很大的问题。若是C中有覆盖A的方法,就不会访问到C,由于深度优先,会先访问A。新式类算法算法,依然采用了深度优先,解决了重复问题,可是同经典算法同样,么有解决继承和单调性的问题。
单调性:若是在D的解析顺序中,B排在A的前面,那么在D的全部子类里,也必须知足这个顺序。
C3算法,解决了继承的单调性,它阻止建立以前版本产生的二义性代码,求得MRO是为了线性化,且肯定了顺序。关于MRO和C3能够参考:Python的多重继承问题-MRO和C3算法
class A:pass class B(A):pass class C(A):pass class D(B):pass class MyClass(D,C):pass print(MyClass.mro()) # [<class '__main__.MyClass'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
注意:
当类不少,继承很复杂的状况下,继承路径太多,很难说清什么样的继承路线。Python语法是容许多继承的,可是Python代码是解释执行,只有执行时,才发现错误,若是团队协做开发,而且引入多继承,那么代码将有可能会变得不可控。因此在Python平常开发规范中建议:
从一个需求开始了解Mixin。现有以下继承关系:
假设已经有了三个类:
class Animal: def __init__(self, name): self.name = name def talk(self): # 抽象方法,没有真正被实现,须要子类本身去实现 raise NotImplementedError() # raise表示抛出异常,NotImplementedError表示没有被实现。 class Human(Animal): pass class Monkey(Animal): pass
解释:
没有实现的方法称为抽象方法,拥有抽象方法的类,叫作抽象类(抽象类不是用来实例化的,而是用来继承的,因此又叫作抽象基类)
子类直接执行talk方法时会产生异常(方法没有被实现)
Python中若是采用上面的方式定义抽象方法,子类能够不实现,可是到子类使用该方法的时候才会报错。
Animal类是抽象基类,基类的方法能够不具体实现,由于它未必适合全部子类,在子类中通常须要重写。Human类和Monkey类属于Animal的子类,如今须要为Human类添加说话的功能,该怎么办呢?若是在Humman类上直接添加,虽然能够,可是却违反了OCP原则,因此咱们只能继承了
下面对代码进行改写
class Animal: def __init__(self, name): self.name = name def talk(self): # 抽象方法,没有真正被实现,须要子类本身去实现 raise NotImplementedError() class Human(Animal): pass class Monkey(Animal): pass class TalkHuman(Human): def talk(self): print('{} say something'.format(self.name)) daxin = TalkHuman('daxin') daxin.talk()
疑问:看似完成了需求,可是若是Human又要唱歌、跳舞、吃饭等方法呢?每次都要继承吗?这样类会不会太多了?可否用其余的方法呢?
前面咱们利用装饰器为函数新增了功能,在Python中一切皆对象,函数和类都是对象,那么咱们是否能够利用装饰器为类添加新功能呢?答案固然是能够的。使用装饰器为Human类添加talk方法:
class Animal: def __init__(self, name): self.name = name def talk(self): # 抽象方法,没有真正被实现,须要子类本身去实现 raise NotImplementedError() def talkhuman(cls): # def talk(self): # print('{} say I Love You'.format(self.name)) # cls.talk = talk cls.talk = lambda self: print('{} say I Love You'.format(self.name)) return cls @talkhuman # Human = talk(human) class Human(Animal): pass class Monkey(Animal): pass daxin = Human('daxin') daxin.talk()
使用柯里化很容易就能够写出为类添加方法的装饰器,这种装饰器还有一个好处,哪里须要talk功能,直接装饰就好。有多个功能的话,那就写多个装饰器。
class Animal: def __init__(self, name): self.name = name def talk(self): # 抽象方法,没有真正被实现,须要子类本身去实现 raise NotImplementedError() def talkhuman(cls): cls.talk = lambda self: print('{} say I Love You'.format(self.name)) return cls def sleephuman(cls): cls.sleep = lambda self: print('{} will sleep with you'.format(self.name)) return cls @sleephuman @talkhuman class Human(Animal): pass class Monkey(Animal): pass daxin = Human('daxin') daxin.talk() daxin.sleep()
先来看代码:
class Animal: def __init__(self, name): self.name = name def talk(self): # 抽象方法,没有真正被实现,须要子类本身去实现 raise NotImplementedError() class TalkMixin: def talk(self): print('{} say I Love You too'.format(self.name)) class Human(Animal): pass class Monkey(Animal): pass class TalkMan(TalkMixin, Human): pass daxin = TalkMan('daxin') daxin.talk()
PS: 感受就是写了一个类给别人继承了?
Mixin体现的就是一种组合的设计模式
,本质上就是多继承实现的。核心思想就是把其它类混合进来,同时带来了类的属性和方法。这里的Mixin看起来和装饰器的效果是同样的,也没什么特别的,可是Mixin是类,它能够继承。
class Animal: def __init__(self, name): self.name = name def talk(self): # 抽象方法,没有真正被实现,须要子类本身去实现 raise NotImplementedError() class TalkMixin: def talk(self): print('{} say I Love You'.format(self.name)) class SingMixin(TalkMixin): # 经过继承来添加新的功能 def sing_a_song(self): print('{} want sing a song'.format(self.name)) super(SingMixin, self).talk() print('Go out,Now') class Human(Animal): pass class Monkey(Animal): pass class TalkMan(TalkMixin, Human): pass class SingMan(SingMixin,Human): pass daxin = SingMan('daxin') daxin.sing_a_song()
使用原则:
使用时Mixin类一般在继承列表的第一个位置
。小结: