一、类的继承
- 面向对象三要素之一,继承Inheritance
- class Cat(Animal)这种形式就是从父类继承,继承可以让子类从父类获取特征(属性和方法)
- 在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码,多复用,子类可以定义自己的属性和方法
class Animal: def __init__(self,name): self._name = name def shout(self): print('{} shout'.format(self.__class__.__name__)) @property #属性装饰器 def name(self): return self._name a = Animal('monster') a.shout() class Cat(Animal): pass cat = Cat('garfield') cat.shout() print(cat.name) 父类: Animal就是Cat的父类,也称为基类,超类 子类:Cat就是Animal的子类,也称为派生类 如果类定义时,没有基类列表,等同于继承自object,在python3中,object类是所有对象的根基类
1、查看继承的特殊属性和方法
- __base__ : 类的基类
- __based__ : 类的基类元组
- __mro__ : 显示方法查找顺序,基类的元组
- mro()方法 : 显示方法查找顺序,基类的元组
- __subclasses__() : 类的子类列表
2、继承中的访问控制
- 从父类继承,自己没有的就可以到父类中找
- 私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类的__dict_-中
- 继承时,公有的,子类和实例都可以随意访问,私有成员被隐藏了,子类和实例不可直接访问
- 当私有变量所在的类内的方法中可以访问这个私有变量
- 属性查找属性:实例的dict--》类的dict--》父类dict
举例: class Animal: __COUNT = 0 HEIGHT = 0 def __init__(self,age,weight,height): self.__COUNT += 1 self.age = age self.__weight = weight self.HEIGHT = height def eat(self): print('{} eat'.format(self.__class__.__name__)) def __getweight(self): print(self.__weight) @classmethod def showcount1(cls): print(cls.__COUNT) @classmethod def __showcount2(cls): print(cls.__COUNT) class Cat(Animal): NAME = 'CAT' c = Cat(3,5,15) c.eat() print(c.HEIGHT) #print(c.__COUNT) #私有的不可访问 #c.__showweight() #私有的不可访问 c.showcount1() #c.__showcount2() #私有的不可访问 print(c.NAME) print("{}".format(Animal.__dict__)) print("{}".format(Cat.__dict__)) print(c.__dict__) print(c.__class__.mro())
3、方法的重写、覆盖overrride
class Animal: def shout(self): print('Animal shout') class Cat(Animal): #覆盖了父类方法 def shout(self): print('miao') #覆盖子类自身的方法,显示调用了父类的方法 def shout(self): print(super()) print(super(Cat,self)) super().shout() super(Cat,self).shout() c = Cat() c.shout()
4、继承中的初始化
class A: def __init__(self,a): self.a = a class B(A): def __init__(self,b,c): self.b = b self.c = c def printv(self): print(self.b) print(self.a) # 报错 f = B(200,300) print(f.__dict__) print(f.__class__.__bases__) f.printv() 上例代码可知:如果类B定义时声明继承自类A,则在类B中__bases__中是可以看到类A,但是这和是否调用类A的构造方法是两回事
1、如果子类B没有定义init方法,初始化的时候会自动调用基类A是init方法 class A: def __init__(self): self.a1 = 'a1' self.__a2 = 'a2' print('A init') class B(A): pass b = B() print(b.__dict__) 2、如果子类B定义了init方法,实例的初始化不会调用父类的初始化init方法 class A: def __init__(self): self.a1 = 'a1' self.__a2 = 'a2' print('A init') class B(A): def __init__(self): self.b1 = 'b1' print('B init') b = B() print(b.__dict__) 3、通过上面分析,子类不会调用父类init方法,这导致没有实现继承效果,所以在子类的__init__方法中, 应该显式调用父类的__init__方法 class Animal: def __init__(self,age): print('Animal init') self.age = age def show(self): print(self.age) class Cat(Animal): def __init__(self,age,weight): #调用父类的__init__方法顺序决定着show方法的结果 super().__init__(age) print('Cat init') self.age = age + 1 self.weight = weight c = Cat(10,5) c.show() 注意、调用父类的__init__方法,出现在不同的位置,
二、多继承
- OCP原则:多继承,少修改;继承的用途:增强基类,实现多
1、多态
- 在面向对象中,父类,子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同的表现,就是多态
- 一个类继承自多个类就是多继承,它将具有多个类的特征
2、多继承弊端
- 多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性带来冲突
- 如同一个孩纸继承了来自父母双方的特征
- 多继承的实现会导致编译器设计的复杂度增加,所以现在很多语言也舍弃了类的多继承
3、python多继承实现
- 多继承带来路径选择问题,究竟继承哪个父类的特征
- python使用MRO(method resolution order)解决基类搜索顺序问题
4、多继承的缺点
- 当类很多,继承复杂的情况下,继承路径太多,很难说清楚什么样的继承路径
- python语法是允许多继承,但是python代码是解释执行,只有执行到的时候才发现错误
举例: class Printable: def _print(self): print(111,self.content) class Document: #第三方库,不允许修改 def __init__(self,content): self.content = content class Word(Document): pass #第三方库,不允许修改 class Pdf(Document): pass #第三方库,不允许修改 class PrintableWord(Printable,Word): pass print(222,PrintableWord.__dict__) print(333,PrintableWord.mro()) pw = PrintableWord('test string') pw._print()
5、用装饰器增强
- 用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰它
- 优点:简单方便,在需要的地方动态增加,直接使用装饰器
def printable(cls): def _print(self): print(self.content,'装饰器') cls.print = _print return cls class Document: #第三方库,不允许修改 def __init__(self,content): self.content = content class Word(Document): pass #第三方库,不允许修改 class Pdf(Document): pass #第三方库,不允许修改 @printable #先继承,后装饰 class PrintableWord(Word): pass print(PrintableWord.__dict__) print(PrintableWord.mro()) pw = PrintableWord('test string') pw.print()
6、Mixin的引用
- Mixin本质上就是多继承实现的,是一种组合的设计模式
- 在面向对象的设计中,一个复杂的类往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起
- 从设计的模式的角度来说,多组合,少继承
举例: class PrintableMixin: def print(self): print(self.content,'Mixin') def printable(cls): def _print(self): print(self.content,'装饰器') cls.print = _print return cls class Document: #第三方库,不允许修改 def __init__(self,content): self.content = content class Word(Document): pass #第三方库,不允许修改 class Pdf(Document): pass #第三方库,不允许修改 class PrintableWord(PrintableMixin,Word): pass print(PrintableWord.__dict__) print(PrintableWord.mro()) pw = PrintableWord('test string') pw.print() Mixin就是其他类混合进来,同时带来了类的属性和方法,这里看来Mixin类和装饰器效果一样,也没有什么特别 但是Mixin是类,就可以继承
7、Mixin类的使用原则
- Mixin类中不应该显式的出现__init__初始化方法
- Mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
- Mixin类的祖先类也应是Mixin类,使用时,Mixin类通常在继承列表的第一个位置