2016-07-28 39 views
5

爲了吸引您的眼球:元類可以被調用嗎?

我認爲文檔可能是錯的!

根據Python 2.7.12文檔,3.4.3。定製類creation¶

__metaclass__此變量可以是任何調用接受 論據名,鹼和字典。在創建課程時,使用可調用的 代替內置的type()

版本2.2中的新功能。

然而,this article認爲:

Q:哇!我可以使用任何類型的對象作爲__metaclass__嗎?

A:否。它必須是基礎對象類型的子類。 ...

所以,我做我自己的一個實驗:

class metacls(list): # <--- subclassing list, rather than type 
    def __new__(mcs, name, bases, dict): 
     dict['foo'] = 'metacls was here' 
     return type.__new__(mcs, name, bases, dict) 

class cls(object): 
    __metaclass__ = metacls 
    pass 

這給了我:

Traceback (most recent call last): 
    File "test.py", line 6, in <module> 
    class cls(object): 
    File "test.py", line 4, in __new__ 
    return type.__new__(mcs, name, bases, dict) 
TypeError: Error when calling the metaclass bases 
    type.__new__(metacls): metacls is not a subtype of type 

所以是文檔真的錯了嗎?

回答

5

不,任何可調用的都可以。在你的情況下,type.__new__()方法有你違反的限制;這與您分配給__metaclass__的內容無關。

函數是可以調用的:

def metaclass_function(name, bases, body): 
    return type(name, bases, body) 

這一個剛剛返回的type()(不type.__new__())的結果,但它是只是調用。返回值使用作爲類別。你真的可以返回任何東西:

>>> class Foo(object): 
...  __metaclass__ = lambda *args: [] 
... 
>>> Foo 
[] 

這裏可調用產生的列表實例,所以Foo綁定到一個列表。不是很有用,但__metaclass__只是調用產生東西,而且直接使用。

在您的例子中,第一個參數是type.__new__(),這是調用失敗。 mcs綁定到list,而不是(的子類)。 type.__new__()可自由設置此類限制。

現在,因爲元類仍然與類對象綁定(type(ClassObj)返回它),並且它在解析類對象(其中屬性在類MRO中不可用)的屬性查找時使用,所以它是通常是一個鳴響好主意,使其成爲type的一個子類別,因爲這可以讓您正確實施諸如__getattribute__之類的內容。正是由於這個原因,type.__new__()限制了第一個參數可以傳入的內容;這是type()附加到返回的類對象的第一個參數。

+0

「綁定到」的意思是「一個子類」,對吧? –

+0

@sunqingyao:不,綁定表示分配給。賦值'Foo = []'被執行。一個'class'語句基本上被視爲一個函數;正如函數中一樣執行主體,並將所有本地名稱提取到字典中。然後通過調用'bodydict.get('__ metaclass__',type)'(所以'__metaclass__'的值或者如果缺少'type'')並傳入類名稱,類的基類,以及身體字典。 *無論返回*分配給班級的名稱。綁定是技術術語;其他的東西也綁定了,比如'import'或'for'。 –

+0

但是通過說「mcs'綁定到'list'」,你似乎意思是「mcs'是'list'的子類的一個實例。 –

相關問題