0

考虑以下示例代码:

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))

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

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

然而,这不是因为__name__是一个 dunder 方法,也不是因为@property@abstractmethod装饰器不能很好地协同工作,因为如果我删除__add__from的实现Sub,它不会让我实例化它。(我知道__add__通常不是属性,但我想使用“真正的” dunder 方法)如果我删除 and 的实现,也会发生相同的预期__str__行为foo。只能__name__以这种方式行事。

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

4

1 回答 1

1

__name__类通过以下数据描述符具有属性type

>>> 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__并且有自己 a __name__)。

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

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

在 的实例上SubBase.__name__然后找到实现,因为实例描述符规则只考虑类和基类,而不考虑元类型。

于 2016-12-15T21:50:52.053 回答