类的继承
基本概念
在面向对象中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码,多复用,子类可以定义自己的属性和方法。
继承
class Cat(Animal)这种形式就是从父类继承,继承可以让子类从父类获取特征(属性和方法)
父类
Animal就是Cat的父类,也称为基类、超类
子类
Cat就是Animal的子类,也称为派生类
定义格式
class 子类名(基类1[,基类2,...]):语句块
如果类定义时,没有基类列表,等同于继承自object,在Python3中,object类是所有对象的根基类。
查看继承的特殊属性和方法
| 特殊属性和方法 | 含义 | 示例 |
|---|---|---|
| base | 查看类的基类 | |
| bases | 查看类的基类元组 | |
| mro | 显示方法查找顺序,基类的元组 | |
| mro()方法 | 同上 | int.mro() |
| subclasses() | 查看类的子类列表 | int.subclasses() |
class Animal:def __init__(self, name):self._name = name@propertydef name(self):return self._nameclass Cat(Animal):passtom = Cat('tom') # 实例化print(tom.name) # 通过子类访问父类的属性# tomprint(Cat.__base__) # 查看子类的父类# <class '__main__.Animal'>print(Cat.__bases__) # 查看子类的父类元组# (<class '__main__.Animal'>,)print(Cat.__mro__) # 查看类的查找方式,返回元组# (<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>)print(Animal.__subclasses__()) # 查看子类的列表# [<class '__main__.Cat'>]
- 从父类继承,子类没有的,从父类中找。
- 私有的都是不可以访问的,但本质上依然是改了个名称放在这个属性所有类的dict_中
- 属性查找顺序:
实例的__dict__>>类的__dict__>>父类的__dict__,如果没都没找到,就会抛出异常。
方法的重写、覆盖
class Animal:
def shout(self):
print('Animal Shout!')
class Cat(Animal):
# 覆盖父类方法
def shout(self):
print(super())
super().shout() # 用来调用父类的一个方法
print('Cat shout')
c = Cat()
c.shout()
# <super: <class 'Cat'>, <Cat object>>
# Animal Shout!
# Cat shout
- 在单继承时,
super().__shout__()``与Animal.__shout__()是一样的,super()避免了显示调用 - 在多继承中,super()获取的是继承顺序中的下一个类
多继承
概念
一个类继承自多个类就是多继承,将具有多个类的特征
OCP原则:多继承,少修改
继承的用途:增强基类,实现多态
多态:在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同的表现,就是多态。
多继承的弊端
多继承引入了复杂性,可能带来二义性,例如,猫和狗都继承自动物类,现在有一个类多继承了猫和狗类,猫和狗都有shout方法,子类究竟应该继承谁的shout呢。
实现多继承的语言,要解决二义性,一般通过查找父类的顺序,深度优先或者广度优先。
多继承带来路径选择问题,Python使用MRO(method resolution order)解决基类搜索问题。
MRO搜索算法
- Python2.2之前,按照定义从左至右,深度优先策略,左图中的MRO是MyClass,D,B,A,C,A
- Python2.2,新式类算法,重复的只保留最后一个,左图中的MRO是MyClass,D,B,C,A,Object
- Python2.3之后,C3算法,在类被创建出来的时候,就计算出一个MRO有序列表,为Python3唯一支持的算法,左图中的MRO是MyClass,D,B,C,A,Object,C3算法旨在解决多继承的二义性。
多继承的进化
需求:
类有下面的继承关系,Document类是其他文档类的抽象基类,word、pdf类是Document的子类,需求是为Document提供打印功能。
思路:
在Document中提供print方法
- 基类的方法不应该具体实现,因为它未必适合子类的打印,子类需要覆盖重写
需要在打印的子类上增加
- 如果在子类上直接增加,违反了OCP原则,所以应该继承后增加。因此有了下图
方案一:使用类的继承
class Document:
def __init__(self, content):
self.content = content
def print(self):
print(self.content)
class Word(Document): # 继承
pass
class PrintableWord(Word): # 继承后增加功能
def print(self):
print('Word print {}'.format(self.content))
class Pdf(Document):
def print(self):
pass
print(PrintableWord.mro())
word = PrintableWord('test abc')
word.print()
# [<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
# Word print test abc
- 使用上述方法会发现,如果需要的功能很多,如序列化、网络传输,序列化又分为pickle、json等,使用上述方式实现,需要的类太多了,继承的方式就很繁琐了。
方案二:使用装饰器
用装饰器增强一个类,把功能给附加上去,哪个类需要,就装饰哪个类
def printable(cls):
def _print(self):
print(self.content, '装饰器')
cls.echo = _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.mro())
# [<class '__main__.PrintableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
print(PrintableWord.__dict__)
# {'__module__': '__main__', '__doc__': None, 'echo': <function printable.<locals>._print at 0x000002C67B6AB510>}
pw = PrintableWord('test word content')
pw.echo()
# test word content 装饰器
- 此方式的优点是在需要的地方动态增加,直接使用装饰器。
方案三:使用Mixin混合类
class PrintableMixin:
def print(self):
print(self.content, 'Mixin')
class SuperPrintableMixin(PrintableMixin): # 使用混合类对PrintableMixin重写
def print(self):
print('~'*20)
super().print()
print('~'*20)
class Document:
def __init__(self, content):
self.content = content
def print(self):
print(self.content, 'Document') # 根据mro顺序,下面的调用就不会再查找Document中的print方法
class Word(Document): pass
class Pdf(Document): pass
class PrintableWord(SuperPrintableMixin, Word): pass # 此处Mixin类PrintableMixin需放置在左边
print(PrintableWord.mro())
pw = PrintableWord('test word')
pw.print()
- Mixin本质上就是多继承实现的,体现的是一种组合的设计模式
- 在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能又来自不同的类提供,这就需要很多类组合在一起。实现多组合,少继承
Mixin类的使用原则
- Mixin类中不应该显式出现
__init__初始化方法 - Mixin类通常不能独立工作,因为它是准备混入别的类的部分功能实现
- Mixin类的祖先类也是Mixin类
- Mixin类中不应该显式出现
- 使用时,Mixin类通常在继承列表的第一个位置,例如
class PrintableWord(SuperPrintableMixin, Word)
