I want to have an instance of class registered when the class is defined. Ideally the code below would do the trick.
registry = {} def register( cls ): registry[cls.__name__] = cls() #problem here return cls @register class MyClass( Base ): def __init__(self): super( MyClass, self ).__init__()
Unfortunately, this code generates the error NameError: global name 'MyClass' is not defined
.
What's going on is at the #problem here
line I'm trying to instantiate a MyClass
but the decorator hasn't returned yet so it doesn't exist.
Is the someway around this using metaclasses or something?
__init_subclass__(cls) method is called on a given class each time a subclass cls for that class is created. Syntax. Minimal Example. Class Hierarchy with __init_subclass__()
Class registration is a helpful pattern for building modular Python programs. Metaclasses let you run registration code automatically each time your base class is subclassed in a program. Using metaclasses for class registration avoids errors by ensuring that you never miss a registration call.
To create your own metaclass in Python you really just want to subclass type . A metaclass is most commonly used as a class-factory. When you create an object by calling the class, Python creates a new class (when it executes the 'class' statement) by calling the metaclass.
Yes, meta classes can do this. A meta class' __new__
method returns the class, so just register that class before returning it.
class MetaClass(type): def __new__(cls, clsname, bases, attrs): newclass = super(MetaClass, cls).__new__(cls, clsname, bases, attrs) register(newclass) # here is your register function return newclass class MyClass(object): __metaclass__ = MetaClass
The previous example works in Python 2.x. In Python 3.x, the definition of MyClass
is slightly different (while MetaClass
is not shown because it is unchanged - except that super(MetaClass, cls)
can become super()
if you want):
#Python 3.x class MyClass(metaclass=MetaClass): pass
As of Python 3.6 there is also a new __init_subclass__
method (see PEP 487) that can be used instead of a meta class (thanks to @matusko for his answer below):
class ParentClass: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) register(cls) class MyClass(ParentClass): pass
[edit: fixed missing cls
argument to super().__new__()
]
[edit: added Python 3.x example]
[edit: corrected order of args to super(), and improved description of 3.x differences]
[edit: add Python 3.6 __init_subclass__
example]
In python 3.6 simpler customization of class creation was introduced (PEP 487).
An
__init_subclass__
hook that initializes all subclasses of a given class.
Proposal includes following example of subclass registration
class PluginBase: subclasses = [] def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.subclasses.append(cls)
In this example,
PluginBase.subclasses
will contain a plain list of all subclasses in the entire inheritance tree. One should note that this also works nicely as a mixin class.
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