python白鹅类型_fluent python 11.10节 鹅的行为有可能像鸭子

Alex 在他写的“水禽和抽象基类”一文中指出,即便不注册,抽象基类也 能把一个类识别为虚拟子类。下面是他举的例子,我添加了一些代码, 使用 issubclass 做测试:

>>> class Struggle:

... def __len__(self): return 23

...

>>> from collections import abc

>>> isinstance(Struggle(), abc.Sized)

True

>>> issubclass(Struggle, abc.Sized)

True

经 issubclass 函数确认(isinstance 函数也会得出相同的结 论),Struggle 是 abc.Sized 的子类,这是因为 abc.Sized 实现了 一个特殊的类方法,名为 __subclasshook__。__subclasshook__会改变isinstance和issubclass的行为

class Sized(metaclass=ABCMeta):

__slots__ = ()

@abstractmethod

def __len__(self):

return 0

@classmethod

def __subclasshook__(cls, C):

if cls is Sized:

# 对 C.__mro__ (即 C 及其超类)中所列的类来说,如果类的__dict__ 属性中有名为 __len__ 的属性……

if any("__len__" in B.__dict__ for B in C.__mro__):

# 返回 True,表明 C 是 Sized 的虚拟子类。

return True

return NotImplemented

__subclasshook__ 在白鹅类型中添加了一些鸭子类型的踪迹。我们可 以使用抽象基类定义正式接口,可以始终使用 isinstance 检查,也可 以完全使用不相关的类,只要实现特定的方法即可(或者做些事情让 __subclasshook__ 信服)。当然,只有提供 __subclasshook__ 方 法的抽象基类才能这么做。

在自己定义的抽象基类中要不要实现 __subclasshook__ 方法呢?可 能不需要。我在 Python 源码中只见到 Sized 这一个抽象基类实现了 __subclasshook__ 方法,而 Sized 只声明了一个特殊方法,因此只 用检查这么一个特殊方法。鉴于 __len__ 方法的“特殊性”,我们基本 可以确定它能做到该做的事。但是对其他特殊方法和基本的抽象基类来 说,很难这么肯定。例如,虽然映射实现了 __len__、__getitem__ 和 __iter__,但是不应该把它们视作 Sequence 的子类型,因为不能 使用整数偏移值获取元素,也不能保证元素的顺序。当 然,OrderedDict 除外,它保留了插入元素的顺序,但是不支持通过 偏移获取元素。

在你我自己编写的抽象基类中实现 __subclasshook__ 方法,可靠性 很低。我可不相信随便一个实现或继承了 load、pick、inspect 和 loaded 的类(如 Spam)的行为一定像 Tombola。程序员最好让 Spam 继承 Tombola,至少也要注册(Tombola.register(Spam)),从而确 保这一点。当然,自己实现的 __subclasshook__ 方法还可以检查方 法签名和其他特性,但我觉得不值得这么做。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值