python-鸭子模型、白鹅类型

231 阅读2分钟

鸭子类型就是对象的类型无关紧要,只要实现了特定的协议(接口)即可

鸭子类型是动态类型的一种风格。在这种风格中,一个重要的原则是:“面向接口编程,而不是面向实现编程”

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 方法,一切都会运行正常,但是如果没有,那就会直接报错。

所以,鸭子类型必须遵循编码约定,文档和测试驱动的方法