2011-02-25 57 views
13

爲了設置類的元類,我們使用__metaclass__屬性。在定義類時使用元類,因此在類定義沒有作用之後顯式地設置元類。有沒有方法在類定義之後設置元類?

這是我嘗試明確設置元類時發生的情況;

>>> class MetaClass(type): 
    def __new__(cls, name, bases, dct): 
     dct["test_var"]=True 
     return type.__new__(cls, name, bases, dct) 
    def __init__(cls, name, bases, dct): 
     super(MetaClass, cls).__init__(name, bases, dct) 


>>> class A: 
    __metaclass__=MetaClass 


>>> A.test_var 
True 
>>> class B: 
    pass 

>>> B.__metaclass__=MetaClass 
>>> B.test_var 

Traceback (most recent call last): 
    File "<pyshell#20>", line 1, in <module> 
    B.test_var 
AttributeError: class B has no attribute 'test_var' 

我能想到的最好的辦法是重新定義整個班級,並添加__metaclass__屬性動態莫名其妙。或者你是否知道在類定義之後設置metaclass的更好方法?

回答

11

您可以在創建類後改變元類的方式與您可以更改對象類的方式相同,但是您會遇到很多問題。對於初學者來說,最初的元類需要與type不同,不會調用新元類的__init____new__(儘管您可以手動調用__init__或執行__init__的作業)。

可能改變元類是最麻煩的方式是從頭重新創建類:

B = MetaClass(B.__name__, B.__bases__, B.__dict__) 

但如果你堅持動態變化的元類,你首先需要有一個臨時的定製元類,定義b :

class _TempMetaclass(type): 
    pass 

class B: 
    __metaclass__ = _TempMetaclass # or: type('temp', (type,), {}) 

然後你就可以定義元類這樣的:

class MetaClass(type): 
    def __init__(cls, *a, **kw): 
     super(MetaClass, cls).__init__(*a, **kw) 
     cls._actual_init(*a, **kw) 
    def _actual_init(cls, *a, **kw): 
     # actual initialization goes here 

然後像做:

B.__class__ = MetaClass 
MetaClass._actual_init(B, B.__name__, B.__bases__, B.__dict__) 

您還需要確保在類的所有初始化完成_actual_init。您還可以將classmethod添加到爲您更改元類的元類。

這兩種解決方案都有B的基礎會受到限制的輕微缺點 - 它們需要兼容原始元素和新元類,但我想這不是問題。

+2

感謝您的回答,「B = MetaClass(B .__ name__,B .__ bases__,B .__ dict __)」正是我所需要的。 – 2011-02-25 19:35:58

-5

我不能現在就測試了這一點(我沒有在我的機器訪問到Python現在:-(),但除非元類是imutable,或許你可以嘗試這樣的事:

>>> class B: 
     pass 
>>> setattr(B, "__metaclass__", MetaClass) 

同樣,我不能測試它現在,所以我道歉,如果這是一個無效的答覆,只是想幫忙。:)

+0

不幸的是,這是行不通的。 – 2011-02-25 17:57:09

+0

不僅它不起作用,而且問題也是荒謬的。 Metaclass用於在創建類時鉤住某些東西,在類創建後更改它並不合理。 – 2013-08-27 03:03:42

2

可以認爲元類描述class聲明做什麼。這就是爲什麼以後設置元類是不可能的:類代碼已經被轉換爲類型對象,改變它爲時已晚。

你可以簡單地繼承原來的類型加元類:爲什麼你需要這個

class C(B): 
    __metaclass__=MetaClass 
+0

所以我想這個問題應該是,如果有一種方法採取類型對象,並修改它,使 – 2011-02-25 18:03:52

+1

@funktku:修改它如何?您可以將屬性和方法添加到任何類型,即'B.test_var = True'。 – 2011-02-25 18:34:09

-1

?我不相信這是可能的,但如果你能解釋用例,我可能會提出一個替代方案。

沿着線重新定義了整個類,你可以做的是這樣的:

import types 
NewClass = types.ClassType('NewClass', (object,), {'__metaclass__':MetaClass}) 

然後就所有的舊方法複製過來:

for item in dir(OldClass): 
    setattr(NewClass, item, getattr(OldClass, item)) 
+0

將屬性設置爲'{'__metaclass __':MetaClass}'不實現MetaClass。 – 2011-02-25 18:00:39

+0

人。我想這種說法是有道理的。你只需要做MetaClass(...),不是嗎?我從來沒有試過這樣使用元類。實驗時間...... – tangentstorm 2011-02-25 18:04:47

5

元類的目的ISN不要修改屬性訪問權限,而要自定義類的創建。 __metaclass__屬性定義了用於從類定義構造類型對象的可調用對象,默認爲type()。因此,該屬性僅在課程創建評估。在以後的任何時候改變它顯然沒有任何意義,因爲您無法爲已創建的類自定義類創建。詳情請參閱Python Language Reference, 3.4.3 Customizing class creation

元類並不是您明顯想要做的事情的正確工具。如果你只是想添加一個新的屬性到一個已經存在的類中,爲什麼不分配它呢?

相關問題