python 3.2插件工厂:从类/元类实例化

python 3.2 plugin factory: instantiation from class/metaclass

我正在从这里的信息中搜索:子类中没有调用元类

我的问题是无法使用这个类注册表创建对象的实例。如果我使用"常规"构造方法,那么它似乎可以正确地实例化对象;但是当我尝试使用与注册表关联的类对象时,我会得到一个错误,即传递的参数数量不正确。(似乎在调用元类new而不是我的构造函数…??)

我不清楚它为什么会失败,因为我认为我应该能够使用"可调用"语法从类对象创建实例。

似乎我要把元类放到注册表中,而不是类本身?但在新的调用中,我看不到访问类本身的简单方法。

下面是我的代码示例,它未能实例化变量"d":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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)

谢谢你的帮助。


我认为您遇到的问题是,传递给元类构造函数的cls参数实际上是对元类的引用,而不是正在创建的类的引用。由于__new__PluginMetaclass的一个类方法,它与该类的关联就像任何常规类方法一样。您可能想注册从super(PluginMetaclass, cls).__new__(..)获得的新创建的类对象。

这个修改后的版本在3.2版上对我有效:

1
2
3
4
5
6
7
8
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()的电话显示了幕后的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
>>> 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还通过在PluginMetaclass中使用__init__而不是__new__解决了这个问题。在大多数情况下,这可能是更好的方法。