Duck-typing 用户定义 类

Duck-typing user-defined classes

所以 Python 的核心语言和内置函数使用了大量鸭子类型。但是对于我们自己的代码,假设我想用一种处理对象 "certain types" 的方法创建自己的 class,使用 duck typing 是否更惯用:

class MerkleTree:

    def method(self, document):
        try:
            hash_ = document.sha256_hash()
        except AttributeError:
            raise TypeError()
        do_smth_with_hash(hash_)

还是只使用普通类型检查更符合习惯:

class MerkleTree:

    def method(self, document):
        if isinstance(document, SHA256Hashable):
            raise TypeError()
        hash_ = document.sha256_hash()
        do_smth_with_hash(hash_)

从纯类型的角度来看,MerkleTree.method 将接受 any 类型的参数; Python没有办法限制。

从鸭子打字的角度来看,您保证

def method(self, document):
    hash_ = document.sha256_hash()
    do_smth_with_hash(hash_)

只要 document.sha256_hash 是可调用的,其 return 值适合 do_smth_with_hash 使用,但 method 不执行任何强制。您的文档说明了调用 method 的先决条件,但是否满足该先决条件取决于调用者,违反它的任何后果都是调用者的问题,而不是 method 的问题。

您可以提供类型提示(使用 Protocol class),以 mypy 等静态类型检查工具可以验证的方式更正式地记录此先决条件。

class ShaHashable(typing.Protocol):
    def sha256_hash(self):
        pass


class MerkleTree:

    def method(self, document: ShaHashable):
        hash_ = document.sha256_hash()
        do_smth_with_hash(hash_)

通常情况下,您不会仅仅为了引发一种不同类型而捕获一个异常。用户通过阅读文档了解到,如果 document 没有合适的方法,method 可能会引发 AttributeError,并且 mypy 可以帮助捕获该错误,而不必 运行代码。

小型甚至中型项目最好使用 duck typing,大型项目最好使用 PEP484+mypy。

EG:

python3 -m mypy --disallow-untyped-calls ${files}

并且:

def pad(number: int) -> str:

顺便说一句,如果您已经有一个没有 PEP484 类型注释的大型项目,您可以使用类似 MonkeyType 的东西来添加它们。