2016-12-15 91 views
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)) 

注意,子类没有实现抽象属性__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

回答

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

+0

@Keozon:检查,我很确定,因为这个类型也有一个描述符。 –

+0

有趣。我将不得不对此进行试验。感谢您的信息! – Keozon