Inheritance
OOP三要素之一,继承
人类和猫都继承自动物类。
个体继承自父母,集成了父母的一部分特征。
在面向对象的世界中,从父类继承,就可以直接拥有弗雷德属性和方法,这样可以减少代码,多复用。子类可以定义自己的属性和方法。
类的继承
对于python来讲,所有的祖先都是object,所有的类都继承自objec
class Animal:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
def shout(self):
print('Animal shout')
class Cat(Animal):# 子类继承父类
def shout(self):
print('miao') # override
class Persian(Cat): #看一下__dict就知道继承了什么属性
def __init__(self):
self.eyes = 'blue'
class Dog(Animal):
def run(self):
print('Dog run')
tom = Cat('tom')
print(tom.name)
print(tom.__dict__)
dog = Dog('ahuan')
print(dog.name)
dog.shout()
ani = Animal('monster')
ani.shout()
cat = Cat('garfield')
cat.shout()
print(cat.name)
tom
{'_name': 'tom'}
ahuan
Animal shout
Animal shout
miao
garfield
增加了代码的复用,减少了冗余。
有自己的特点,先继承,再覆盖
从上面的例子看出,通过继承,子类不用写代码,直接继承了父类的属性和方法。
Inheritance
Superclass父类,fatherclass ,基类
childclass ,派生类
object类,python2.2之后引入的,它是所有类的共同祖先类。
Python2 中为了兼容,分为古典类class A(object):和新式类class A():。
python3之后都是是新式类
新式类都是继承自object的,新式类可以使用super。
父类的就是你的,传什么就是什么,如self。
实例继承的属性,放在实例类的实例上,即实例的一定放在实例上。只是self.的。类的放在类属性上。
私有的都是不可访问的
多继承
OCP(Open-Closed Principle)原则:
Software entities should be open for extention,but closed for modification.
多继承、少修改
继承的用途:增强基类、实现多态
多态:
在OOP中,superclass和childclass是通过继承联系在一起,如果通过一套方法,实现不同表现就是多态。
一个类继承自多个类就是多继承,它将具有多个类的特征。
多继承的弊端及引入MRO
带来路径选择问题,究竟继承哪个superclass的feature
MRO(method resolution order)
Python 使用MRO解决基类搜索顺序问题。
解决base class 搜索顺序
MRO三种resolution order
classic,从左到右,深度优先策略。2.2之前是MyClass,D,B,A,C,A
新式类,重复的只保留最后一个。MyClass,D,B,C,A,object
C3算法,在类被创建出来的时候,就计算出一个MRO有序列表。.MRO,返回一个有序列表。解决了多继承的二义性。
lst = MyClass,D,B,C,A,object
多继承的缺点
类很多,继承复杂,继承路径太多,很难说清什么样的继承路径,语法允许多继承,但解释执行时执行时候才发现错误。
团队协作开发,如果引入多继承,代码不可控。
不论是否支持多继承,都应当避免多继承。
Python的面向对象,太灵活,太开放,注重规矩。
增加需求
不完整的一个类,需要一个临时使用的方法,用上多继承的方法,组合在这个类中。
新建一个需要的子类,可以增加和覆盖原有不便的方法,这时候实例是子类的实例。
需求:
为Document子类提供打印能力
思路:
1、在Document中提供print方法
基类提供的方法不应该具体实现,子类需要覆盖重写。print算是一种能力,不是所有的Document子类都需要。
2、需要打印的子类上增加
继承后增加,不用在子类上直接增加,遵循了OCP原则。但是如果遇到需要不确定的多项功能时,需要增加的子类过多。
3、装饰器
printable相当于把PrintableWord的名字,即这个标示符拿去,就是拿了这个类(函数)对象,作为参数放到printable函数里。给它添加了一个print方法(类对象是可以动态增加类属性的)(数据的叫属性,非数据的叫方法),类对象的__dict,类本身没有变化。
运行时类对象与print函数绑定,将类对象作为第一参数传入。
class Printable:
def _print(self):
print(self.content)
class Document:
def __init__(self, content):
self.content = content
class Word(Document): pass
class Pdf(Document): pass
def printable(cls):
cls.print = lambda self: print(self.content)
return cls
@printable # PrintableWord=printable(PrintableWrod)
class PrintableWord(Wrod): pass
在需要的地方动态增加,直接使用装饰器,不需要对类进行修改。
4、Mixin
原则:Mixin应当覆盖或替换类的功能,在继承列表的最左边。
class PrintableMxixin:
def print(self):
print('--------------------------')
print('{}'.format(self.content))
print('--------------------------')
class Document:
def __init__(self, content):
self.content = content
def print(self):
print(self.content)
class Word(Document): pass
class PrintableWord(Word):pass
class Pdf(Document): pass
class PrintablePdf(PrintableMxixin, Pdf): 如果将Mixin放在右边则改变了mro,使用Document的print方法。
pass
print(PrintablePdf.mro())
pdf = PrintablePdf('teset\nabc')
print(pdf.__dict__)
pdf.print()
[<class '__main__.PrintablePdf'>, <class '__main__.PrintableMxixin'>, <class '__main__.Pdf'>, <class '__main__.Document'>, <class 'object'>]
{'content': 'teset\nabc'}
--------------------------
teset
abc
--------------------------
Mixin就是其他类混合进来,同时带来了类的属性和方法。
Mixin是类,可以继承。
Mixin类的使用原则
Mixin类中不应该显示的出现__init__初始化方法
Mixin类通常不能独立工作,因为它是准备混入别的类中的部分功能实现
Mixin类的祖先类也应该是Mixin类