Python 的PEP 544引入typing.Protocol
了结构子类型,也就是“静态鸭子类型”。
在这个 PEP 关于合并和扩展协议的部分中,声明了
一般的理念是协议大多类似于常规的 ABC,但静态类型检查器会专门处理它们。
因此,人们期望从 的子类继承与期望从 的子类继承的typing.Protocol
方式大致相同abc.ABC
:
from abc import ABC
from typing import Protocol
class AbstractBase(ABC):
def method(self):
print("AbstractBase.method called")
class Concrete1(AbstractBase):
...
c1 = Concrete1()
c1.method() # prints "AbstractBase.method called"
class ProtocolBase(Protocol):
def method(self):
print("ProtocolBase.method called")
class Concrete2(ProtocolBase):
...
c2 = Concrete2()
c2.method() # prints "ProtocolBase.method called"
正如预期的那样,具体的子类Concrete1
继承Concrete2
自method
它们各自的超类。此行为记录在 PEP 的显式声明实现部分中:
要显式声明某个类实现给定协议,可以将其用作常规基类。在这种情况下,一个类可以使用协议成员的默认实现。
...
请注意,显式和隐式子类型之间几乎没有区别,显式子类化的主要好处是“免费”获得一些协议方法。
但是,当协议类实现__init__
方法时,__init__
不会被协议类的显式子类继承。这与继承方法的类的ABC
子类相反:__init__
from abc import ABC
from typing import Protocol
class AbstractBase(ABC):
def __init__(self):
print("AbstractBase.__init__ called")
class Concrete1(AbstractBase):
...
c1 = Concrete1() # prints "AbstractBase.__init__ called"
class ProtocolBase(Protocol):
def __init__(self):
print("ProtocolBase.__init__ called")
class Concrete2(ProtocolBase):
...
c2 = Concrete2() # NOTHING GETS PRINTED
我们看到,Concrete1
继承__init__
自AbstractBase
,但Concrete2
不继承__init__
自ProtocolBase
。这与前面的示例形成对比,其中Concrete1
和都从各自的超类Concrete2
继承。method
我的问题是:
- 不
__init__
被协议类的显式子类型继承的原因是什么?协议类无法__init__
“免费”提供方法是否存在某种类型理论的原因? - 是否有任何关于这种差异的文件?或者它是一个错误?