抽象属性 __name__ 不强制执行



请考虑以下示例代码:

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__时,它会从其父级打印为"抽象":

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

但是,这不是因为__name__是一个 dunder 方法,也不是因为@property@abstractmethod装饰器不能很好地协同工作的一些问题,因为如果我从Sub中删除__add__的实现,它不允许我实例化它。(我知道__add__通常不是一个属性,但我想使用"真正的"dunder方法)如果我删除__str__foo的实现,也会发生相同的预期行为。只有__name__才会这样做。

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

类通过type上的数据描述符具有__name__属性:

>>> Sub.__name__
'Sub'
>>> '__name__' in Sub.__dict__
False

它是一个数据描述符,因为它还可以截获赋值以确保值是字符串。实际值存储在 C 结构上的插槽中,描述符是该值的代理(因此在类上设置新值也不会向__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实例时对属性的测试成功,毕竟类具有该属性:

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

Sub的实例上,可以找到Base.__name__实现,因为实例描述符规则只考虑类和基类,而不考虑元类型。

最新更新