__name__ 上的摘要 属性 未强制执行

Abstract Property on __name__ not enforced

考虑以下示例代码:

from abc import ABC, abstractmethod, abstractproperty

class Base(ABC):

    @abstractmethod
    def foo(self) -> str:
        print("abstract")

    @property
    @abstractmethod
    def __name__(self) -> str:
        return "abstract"

    @abstractmethod
    def __str__(self) -> str:
        return "abstract"

    @property
    @abstractmethod
    def __add__(self, other) -> str:
        return "abstract"


class Sub(Base):

    def foo(self):
        print("concrete")

    def __str__(self):
        return "concrete"

    def __add__(self, other) -> str:
        return "concrete"


sub = Sub()
sub.foo()
sub.__name__
print(str(sub))

请注意,子类没有实现抽象 属性 __name__,实际上当 __name__ 被引用时,它从其父类打印为 "abstract":

>>> sub.foo()
concrete
>>> sub.__name__
'abstract'
>>> print(str(sub))
concrete

然而,这不是因为 __name__ 是一个 dunder 方法,也不是因为 @property@abstractmethod 装饰器不能很好地协同工作,因为如果我删除实现来自 Sub__add__,它不允许我实例化它。 (我知道 __add__ 通常不是 属性,但我想使用 'real' dunder 方法)如果我删除 __str__foo。只有 __name__ 会这样。

__name__ 是什么导致了这种行为?有什么办法解决这个问题,还是我需要让父(抽象)实现手动为我提高 TypeError

类 通过 数据描述符 type:

上具有 __name__ 属性
>>> Sub.__name__
'Sub'
>>> '__name__' in Sub.__dict__
False

它是一个数据描述符,因为它还拦截赋值以确保该值是一个字符串。实际值存储在 C 结构的槽中,描述符是该值的代理(因此在 class 上设置新值也不会向 __dict__ 添加新条目) :

>>> Sub.__name__ = None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign string to NewName.__name__, not 'NoneType'
>>> Sub.__name__ = 'NewName'
>>> Sub.__name__
'NewName'
>>> '__name__' in Sub.__dict__
False

(实际上访问该描述符而不触发它的 __get__ 是不可能的,因为 type 本身没有 __dict__ 并且本身有一个 __name__)。

这导致在创建Sub实例时对该属性的测试成功,class毕竟有该属性:

>>> hasattr(Sub, '__name__')
True

Sub 的实例上,然后找到 Base.__name__ 实现,因为实例描述符规则仅考虑 class 和基础 classes,而不考虑元类型。