【问题标题】:python 3.2 plugin factory: instantiation from class/metaclasspython 3.2插件工厂:从类/元类实例化
【发布时间】:2011-08-21 21:35:18
【问题描述】:

我正在从这里的信息中复制:Metaclass not being called in subclasses

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

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

似乎我将元类放入注册表而不是类本身?但我没有看到在 new 调用中访问类本身的简单方法。

这是我的代码示例,它无法实例化变量“d”:

registry = [] # 子类列表 类插件元类(类型): def __new__(cls, name, bases, attrs): 打印(分类) 打印(名称) registry.append((名称,cls)) return super(PluginMetaclass, cls).__new__(cls, name, bases, attrs) 类插件(metaclass=PluginMetaclass): def __init__(自我,东西): self.stuff = 东西 # 在你的插件模块中 类 SpamPlugin(插件): def __init__(自我,东西): self.stuff = 东西 BaconPlugin 类(插件): def __init__(自我,东西): self.stuff = 东西 c = SpamPlugin(0) b = 培根插件(0) mycls = 注册表[1][1] d = mycls(0)

感谢您的帮助。

【问题讨论】:

    标签: python factory-pattern metaclass


    【解决方案1】:

    我认为您遇到的问题是传递给元类构造函数的cls 参数实际上是对元类的引用,而不是正在创建的类。因为__new__PluginMetaclass 的类方法,所以它与该类相关联,就像任何常规类方法一样。您可能想要注册从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 也通过在 PluginMetaclass 中使用 __init__ 而不是 __new__ 解决了这个问题。在大多数情况下,这可能是更好的方法。

    【讨论】:

    • 好的,格雷格,谢谢。我昨天猜测和测试了几个小时,然后才找到一个更简单的解决方案。将 new 替换为 init,第一个参数是类名而不是元类名。对 super.__init__ 的调用将需要删除第一个参数,否则它是对原始代码的修复,仅涉及将 new 更改为 init。我当然希望文档更清楚地说明元类的可用接口。
    • 我还应该说,我个人倾向于使用 init 而不是 new。 Init 是验证,而 new 是分配。我不想在不必要的细节级别上乱搞,我认为班级注册更像是一个验证步骤。不过,无论哪种方式都绝对有效。
    • 我同意,__init__ 对我来说也感觉更好,并且绝对更适合这个应用程序。我认为一般来说对__new__ 的需求相当少,对于元类来说尤其少见。
    猜你喜欢
    • 1970-01-01
    • 2021-11-27
    • 2019-07-05
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多