鸭子类型就是对象的类型无关紧要,只要实现了特定的协议(接口)即可
鸭子类型是动态类型的一种风格。在这种风格中,一个重要的原则是:“面向接口编程,而不是面向实现编程”
class Duck():
def walk(self):
print('I walk like a duck')
def swim(self):
print('i swim like a duck')
class Person():
def walk(self):
print('this one walk like a duck')
def swim(self):
print('this man swim like a duck')
Person 类拥有跟 Duck 类一样的方法,当有一个函数调用某个类的 walk() 和 swim() 方法,如果函数不去检查对象的类型是 Duck 类还是 Person 类,只要他拥有 walk() 和 swim() 方法,就可以正确的被调用
当我们想自己定义一个新的序列类 LikeSeq,我们就需要实现__getitem__,__len__等方法,然后你创建一个对象 seq=LikeSeq(),你可以像调用其它序列对象一样去用 len(seq) 获得该序列的大小,用 seq[0] 去索引序列的第一个元素。
class LikeSeq(object):
def __getitem__(self, index):
metadata = [0, 1, 2, 3]
return metadata[index]
def __len__(self):
return 10
seq=LikeSeq()
print(len(seq))
print(seq[0])
输出
10
0
白鹅类型
我们可以把鸭子类型看成一种模仿行为,它的行为像某种类型但是它的类型又不是。 白鹅类型是指对接口有明确定义,只要
cls是抽象基类,即cls的元类是abc.ABCMeta,就可以使用isinstance(obj, cls)。
from collections import abc
class LikeSequence: # 鸭子类型
def __init__(self, components):
self._components = components
def __getitem__(self, item):
return self._components[item]
def __len__(self):
return len(self._components)
class RealSequence(abc.Sequence): # 白鹅类型
def __init__(self, components):
self._components = components
def __getitem__(self, item):
return self._components[item]
def __len__(self):
return len(self._components)
>>> obj1 = LikeSequence(list('abcde'))
>>> print(isinstance(obj1, abc.Sequence))
False
>>> obj2 = RealSequence(list('abcde'))
>>> print(isinstance(obj2, abc.Sequence))
True
>>> obj1.count('a') # YourSequence中没有count方法
AttributeError: 'LikeSequence' object has no attribute 'count'
>>> obj2.count('a') # 白鹅类型Sequence继承后能够自动获得抽象基类的方法
1
如果调用的对象提供了 count 方法,一切都会运行正常,但是如果没有,那就会直接报错。
所以,鸭子类型必须遵循编码约定,文档和测试驱动的方法。