I'm riffing from the information here: Metaclass not being called in subclasses
My problem is that I'm unable to create an instance of an object using this class registry. If I use "regular" construction methods, then it seems to instantiate objects correctly; but when I try to use the class object associated with registry, then I get an error that I'm passing an incorrect number of arguments. (Seems to be calling the metaclass new and not my constructor...??)
I'm not clear why it's failing, because I thought I should be able to create an instance from the class object by using "callable" syntax.
Seems I'm getting the metaclass put in the registry and not the class itself? But I don't see an easy way to access the class itself in the new call.
Here is my code example, which fails to instantiate a variable '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)
Thanks for any help.
I think the issue you're having is that the cls
parameter passed to a metaclass constructor is actually a reference to the metaclass and not the class which is being created. Since __new__
is a classmethod of PluginMetaclass
, it's associated with that class just like any regular classmethod. You probably want to be registering the newly created class object you're getting from super(PluginMetaclass, cls).__new__(..)
.
This modified version worked for me on 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
and the print()
calls show what's going on behind the scenes:
>>> 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'>)]
Edit: @drone115b also solved this by using __init__
instead of __new__
in PluginMetaclass
. That's probably the better way to go in most cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With