__name__上的抽象属性未强制执行

时间:2016-12-15 21:48:47

标签: python abstract-class python-3.5 abc

请考虑以下示例代码:

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装饰器的某些问题不能很好地协同工作,因为如果我删除了{的实现来自__add__的{​​1}},它不允许我实例化它。 (我知道Sub通常不是属性,但我想使用真正的“dunder”方法。如果删除__add__和{{__str__的实现,则会出现相同的预期行为1}}。只有foo才会这样。

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

1 个答案:

答案 0 :(得分:1)

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

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

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

>>> 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__。< / p>

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

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

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