2011-05-09 83 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