2016-12-15 93 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