Jas*_*sha 3 protocols abstract-base-class python-3.x python-typing
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"
Run Code Online (Sandbox Code Playgroud)
正如所料,具体子类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
Run Code Online (Sandbox Code Playgroud)
我们看到,Concrete1继承__init__自AbstractBase,但Concrete2不继承__init__自ProtocolBase。这与前面的示例形成对比,其中Concrete1和Concrete2都method从各自的超类继承。
我的问题是:
__init__被协议类的显式子类型继承的基本原理是什么?协议类无法__init__“免费”提供方法是否存在某种类型理论原因?您不能直接实例化协议类。这目前是通过用一种__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
Run Code Online (Sandbox Code Playgroud)
你__init__不会执行,因为它不再存在。
这很奇怪,而且比乍一看的东西还要多——例如,它与多重继承的交互很差,中断super().__init__链。
| 归档时间: |
|
| 查看次数: |
418 次 |
| 最近记录: |