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
类的子类形成对比,后者do继承__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
从AbstractBase
继承了__init__
,但Concrete2
没有从ProtocolBase
继承__init__
。这与前面的例子形成对比,在前面的例子中,Concrete1
和Concrete2
都从它们各自的超类继承method
。
我的问题是:
- 协议类的显式子类型不继承
__init__
的基本原理是什么?是否存在协议类不能提供CCD_ 21方法的某种类型论原因;免费"> - 有关于这种差异的任何文件吗?还是一个bug
您不能直接实例化协议类。目前,这是通过将协议的__init__
替换为一种方法来实现的,该方法的唯一功能是强制执行该限制:
def _no_init(self, *args, **kwargs):
if type(self)._is_protocol:
raise TypeError('Protocols cannot be instantiated')
...
class Protocol(Generic, metaclass=_ProtocolMeta):
...
def __init_subclass__(cls, *args, **kwargs):
...
cls.__init__ = _no_init
您的__init__
不会执行,因为它已不存在。
这很奇怪,而且比乍一看还要麻烦——例如,它与多个继承的交互很差,中断了super().__init__
链。