【问题标题】:Abstract Property on __name__ not enforced__name__ 上的抽象属性未强制执行
【发布时间】:2016-12-15 21:48:46
【问题描述】:

考虑以下示例代码:

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__ 是一个笨拙的方法,也不是因为@property@abstractmethod 装饰器不能很好地协同工作,因为如果我从Sub 中删除__add__ 的实现,它不允许我实例化它。 (我知道__add__ 通常不是属性,但我想使用“真正的”dunder 方法)如果我删除__str__foo 的实现,也会出现同样的预期行为。只有__name__ 有这种行为。

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

【问题讨论】:

    标签: python abstract-class python-3.5 abc


    【解决方案1】:

    类通过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__ 实现,因为实例描述符规则只考虑类和基类,而不考虑元类型。

    【讨论】:

    • @Keozon:检查一下,我很确定,因为类型上还有一个描述符。
    • 有趣。我将不得不对此进行试验。感谢您的信息!
    猜你喜欢
    • 2019-10-13
    • 1970-01-01
    • 2011-10-08
    • 1970-01-01
    • 1970-01-01
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 2023-01-27
    相关资源
    最近更新 更多