-我眼中的- | 鸭子类型 | 白鹅类型 |
---|---|---|
代表什么 | 协议 | 接口 |
特征 | 动态 | 规范 |
所谓鸭子类型,就是说我们需要的鸭子不一定是真正的鸭子。我们只关注我们要求的方面像鸭子。因此我们甚至可以说我们实现的类,它既是鸭子,也是麻雀,甚至还有可能是犀牛。
而白鹅类型的提出则更加的具有规范性:我们判断一个类是不是白鹅,不是根据这个类具体的行为,而是去看这个类是否继承自白鹅基类。即它的体内有没有白鹅的DNA。
因此我们所说的使用鸭子类型和白鹅类型时,其实就是在说: “符合相应的协议就可以||或者你要去继承相应的抽象基类,去实现接口所必须的方法”
作者在讲解猴子补丁的时候运用了一个例子,在一开始就定义的扑克牌类中,想要实现洗牌。
不使用猴子补丁:
shuffle
,用于洗牌使用猴子补丁:
random.shuffle
方法,所以我们只需要设法将类的实例变为参数,所以只需要实现序列协议中的__setitem__
方法。同样是为了实现洗牌功能,两种方式的行为却不太一样。而第二种的行为称之为补丁是比较贴切的,像是在自己没有完全实现的协议上打补丁一样。(实现有缺陷的协议–>实现更完善的协议)
在白鹅类型的定义中提到了抽象基类,但是抽象基类到底是什么呢?
抽象基类也是类。
import abc
class Tombola(abc.ABC):
@abc.abstractmethod
def load(self, iterable):
@abc.abstractmethod
def pick(self):
def loaded(self):
return bool(self.inspect())
def inspect(self):
items = []
while True:
try:
items.append(self.pick())
except LookupError:
break
self.load(items)
return tuple(sorted(items))
@abstractmethod
@abstractmethod
上可以堆叠装饰器@abstractmethod
和def
之间不可以有其他装饰器抽象方法->内部接口->具体方法
@abstractmethod
的是抽象方法,那么剩下的方法是什么呢?子类应该做什么:
我们都知道,如果一个类继承自别的类,那么它就是这个类的子类。
大概还是要遵循一下语法的:
class BingoCage(Tombola):
这个时候就是继承自超类的子类了。如果使用类型检查函数isinstance
去检查也完全没有问题。
但是白鹅类型提供了一个十分动态的方案: 注册
python提供了register
函数来注册一个类为抽象基类的子类。
使用方式有两种:
–> 使用装饰器句法:(python3.3以后)
@Tombola.register
class TomboList(List):
#....
–> 使用函数:
Tombola.register(TomboList)
isinstance
进行类型检查的时候,python会直接返回True在白鹅类型中我们强调的是规则:只有当你全部实现了抽象方法的时候,才可以去继承所谓的抽象基类,实现所谓的接口
但是你肯定会疑惑: 为什么要留一条名为注册的后路,这貌似不符合白鹅类型的标准。
事实上,不仅仅是注册。鸭子类型和白鹅类型都不是独立存在的。它们都是在我们面向对象的过程中产生的。
在自然界中,鹅的行为可以像鸭子 (当然反过来也可以)
举例说明: 如果手动实现了抽象方法,既没有继承也没有注册。会有什么后果。
>>> class Struggle:
...def __len__(self): return 23
...
>>> from collections import abc
>>> isinstance(Struggle(), abc.Sized)
True
>>> issubclass(Struggle, abc.Sized)
True
事实上我们的Struggle
类只是实现了len()
方法而已,但是python解释器却认同了二者的类型。
即: Struggle
是一个 Sized
而这一切的来源,是Sized
类中的一个方法:__subclasshook__
:
一只动物,我们如果说它是某某白鹅,那么它必须有白鹅的DNA。
但是如果我们需要一个鸭子来展出。我们仍然可能可以把这只白鹅的近亲放上去而不被发现有问题。因为某种程度上,这个动物的行为很可能很像鸭子。
以下是人话:
一个实现的类,如果说他是一个某某类型,那么他应该继承该对应的抽象基类。
但是当我们需要一个其他实例的场景中,如果这个类实现了相关的方法,我们也可以把它的实例传进去而不会抛出Error,因为即便类是一个某某抽象基类的子类,它也仍然可以遵守很多其他的协议。