2011-05-09 53 views
9

實例,我從這裏的信息riffing:Metaclass not being called in subclasses蟒蛇3.2插件廠:從類/元類

我的問題是,我無法創建一個使用這個類註冊表中的對象的實例。如果我使用「常規」構造方法,那麼它似乎正確地實例化對象;但是當我嘗試使用與註冊表關聯的類對象時,那麼我得到一個錯誤,我傳遞的參數數量不正確。 (似乎是調用元類而不是我的構造函數...... ??)

我不清楚它爲什麼失敗,因爲我認爲我應該能夠通過使用類對象創建一個實例「可調用」語法。

似乎我得到的元類放在註冊表中,而不是類本身?但我沒有看到在新的調用中訪問類本身的簡單方法。

這裏是我的代碼示例,從而未能實例化一個變量「d」:

 
registry = [] # list of subclasses 

class PluginMetaclass(type): 
    def __new__(cls, name, bases, attrs): 
     print(cls) 
     print(name) 
     registry.append((name, cls)) 
     return super(PluginMetaclass, cls).__new__(cls, name, bases, attrs) 

class Plugin(metaclass=PluginMetaclass): 
    def __init__(self, stuff): 
     self.stuff = stuff 

# in your plugin modules 
class SpamPlugin(Plugin): 
    def __init__(self, stuff): 
     self.stuff = stuff 

class BaconPlugin(Plugin): 
    def __init__(self, stuff): 
     self.stuff = stuff 

c = SpamPlugin(0) 
b = BaconPlugin(0) 
mycls = registry[1][1] 
d = mycls(0) 

感謝您的幫助。

回答

4

我認爲你遇到的問題是傳遞給元類構造函數的cls參數實際上是對元類的引用,而不是正在創建的類。由於__new__是一個PluginMetaclass的分類方法,它就像任何常規的classmethod一樣與該類相關聯。您可能希望註冊您從super(PluginMetaclass, cls).__new__(..)獲得的新創建的類對象。

這個修改後的版本爲我工作在3.2:

class PluginMetaclass(type): 
    def __new__(cls, name, bases, attrs): 
     print("Called metaclass: %r" % cls) 
     print("Creating class with name: %r" % name) 
     newclass = super(PluginMetaclass, cls).__new__(cls, name, bases, attrs) 
     print("Registering class: %r" % newclass) 
     registry.append((name, newclass)) 
     return newclass 

print()來電顯示是怎麼回事幕後:

>>> registry = [] 
>>> 
>>> class Plugin(metaclass=PluginMetaclass): 
...  def __init__(self, stuff): 
...   self.stuff = stuff 
... 
Called metaclass: <class '__main__.PluginMetaclass'> 
Creating class with name: 'Plugin' 
Registering class: <class '__main__.Plugin'> 
>>> class SpamPlugin(Plugin): 
...  def __init__(self, stuff): 
...   self.stuff = stuff 
... 
Called metaclass: <class '__main__.PluginMetaclass'> 
Creating class with name: 'SpamPlugin' 
Registering class: <class '__main__.SpamPlugin'> 
>>> class BaconPlugin(Plugin): 
...  def __init__(self, stuff): 
...   self.stuff = stuff 
... 
Called metaclass: <class '__main__.PluginMetaclass'> 
Creating class with name: 'BaconPlugin' 
Registering class: <class '__main__.BaconPlugin'> 
>>> c = SpamPlugin(0) 
>>> b = BaconPlugin(0) 
>>> mycls = registry[1][1] 
>>> d = mycls(0) 
>>> d 
<__main__.SpamPlugin object at 0x010478D0> 
>>> registry 
[('Plugin', <class '__main__.Plugin'>), 
('SpamPlugin', <class '__main__.SpamPlugin'>), 
('BaconPlugin', <class '__main__.BaconPlugin'>)] 

編輯: @ drone115b也可以通過使用解決了這個__init__而不是__new__PluginMetaclass。在大多數情況下,這可能是更好的方法。

+0

是的,Greg,謝謝。我猜昨天測試了幾個小時,然後發生在一個更簡單的解決方案上。將__new__替換爲__init__,第一個參數是類名,而不是元類名。對super .__ init__的調用將需要刪除第一個參數,否則它是對原始代碼的修復,只涉及將__new__更改爲__init__。我當然希望文檔在metaclasses的可用接口上更加清晰。 – drone115b 2011-05-10 12:36:27

+0

我也應該說,我有一個輕微的個人喜好使用init而不是新的。 Init是一個驗證,而新的是一個分配。我不想在低於必要的細節水平上搞亂,我認爲課堂註冊更像是一個驗證步驟。但無論哪種方式肯定有效。 – drone115b 2011-05-10 12:50:28

+0

我同意,'__init__'對我來說也更好,對於這個應用程序肯定更合適。我認爲對'__new__'的需求在一般情況下是非常少見的,特別是對於元類來說尤其少見。 – 2011-05-10 14:06:38