打字.在显式子类型构造期间未调用协议类"__init__"方法



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"

正如预期的那样,具体子类Concrete1Concrete2从它们各自的超类继承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

我们看到,Concrete1AbstractBase继承了__init__,但Concrete2没有从ProtocolBase继承__init__。这与前面的例子形成对比,在前面的例子中,Concrete1Concrete2都从它们各自的超类继承method

我的问题是:

  1. 协议类的显式子类型不继承__init__的基本原理是什么?是否存在协议类不能提供CCD_ 21方法的某种类型论原因;免费">
  2. 有关于这种差异的任何文件吗?还是一个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__链。

相关内容

  • 没有找到相关文章

最新更新