2015-09-17 102 views
7

我編寫了一個裝飾(出於好奇的緣故)在python中做了一個類抽象。到目前爲止,它看起來會起作用,但我得到了意想不到的行爲。Python抽象裝飾不起作用

用於裝修的想法是這樣的:

from abc import ABCMeta, abstractmethod 

def abstract(cls): 
    cls.__metaclass__ = ABCMeta 
    return cls 

然後,使用該裝飾時,它只是需要定義一個抽象的方法

@abstract 
class Dog(object): 
    @abstractmethod 
    def bark(self): 
     pass 

但是,當我考,我能實例化Dog對象:

d = Dog() 
d.bark() //no errors 
Dog.__metaclass__ //returned "<class 'abc.ABCMeta'>" 

當測試指定__metaclass__直接,它像預期的那樣:

​​

測試:

d = Dog() 

"Traceback (most recent call last): 
    File "<pyshell#98>", line 1, in <module> 
    d = Dog() 
TypeError: Can't instantiate abstract class Dog with abstract methods bark" 

這究竟是爲什麼?

+0

檢查我的答案,這解決了你的問題。感謝這個問題。 –

回答

2

我已經能夠重現您的問題。

ABCMeta .__ new__永遠不會使用您的裝飾器調用。當你真的把它定義爲Dog的元類時,它就會被調用。這是因爲Dog類已經被定義了,所以之後添加ABCMeta實際上並沒有什麼區別,因爲它的沒有在定義時被調用,所以不會將Dog類註冊爲摘要,並且不會發現其抽象方法。我邀請你閱讀ABCMeta。 新的代碼,你會看到它的作用。

然而,我發現這個解決方案,還是讓你的裝飾工作:

def abstract(cls): 
    return ABCMeta(cls.__name__, cls.__bases__, dict(cls.__dict__)) 

這現在將按預期。意思是您必須繼承Dog才能撥打bark

+0

工作,整潔! – Hydren

4

Python參考states

當類定義被讀出,如果__metaclass__定義然後分配給它的調用將被調用,而不是類型()。這允許類或函數將被寫入其中監視器或修改類創建過程

的重要組成部分,是當類定義爲,這意味着你不能事後改變元類,這是隻是裝飾者試圖做什麼,因爲它只是語法糖。

@some_decorator 
class SomeClass(object): 
    pass 

是一樣的:

class SomeClass(object): 
    pass 

SomeClass = some_decorator(SomeClass) 

所以當裝飾被調用時,類定義已經被閱讀。

+0

非常簡潔的答案。儘管Apero的答案給了我一個解決方案,但如果可以的話,我也會將你標記爲「已接受」。 – Hydren