Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to auto register a class when it's defined

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?

like image 928
deft_code Avatar asked Mar 04 '11 03:03

deft_code


People also ask

What is __ Init_subclass __ in Python?

__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__()

What does it mean to register a class in Python?

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.

How do you use metaclasses in Python?

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.


2 Answers

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]

like image 132
dappawit Avatar answered Oct 06 '22 08:10

dappawit


Since python 3.6 you don't need metaclasses to solve this

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.

like image 29
matusko Avatar answered Oct 06 '22 08:10

matusko