Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to set metaclass after the class definition?

In order to set metaclass of a class, we use the __metaclass__ attribute. Metaclasses are used at the time the class is defined, so setting it explicitly after the class definition has no effect.

This is what happens when I try to set metaclasses explicitly;

>>> class MetaClass(type):
    def __new__(cls, name, bases, dct):
        dct["test_var"]=True
        return type.__new__(cls, name, bases, dct)
    def __init__(cls, name, bases, dct):
        super(MetaClass, cls).__init__(name, bases, dct)


>>> class A:
    __metaclass__=MetaClass


>>> A.test_var
True
>>> class B:
    pass

>>> B.__metaclass__=MetaClass
>>> B.test_var

Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    B.test_var
AttributeError: class B has no attribute 'test_var'

The best idea I can think of is to re-define whole class and add the __metaclass__ attribute dynamically somehow. Or do you know a better way set metaclass after the class definition?

like image 935
Utku Zihnioglu Avatar asked Feb 25 '11 17:02

Utku Zihnioglu


People also ask

How do you define a metaclass in Python?

A metaclass in Python is a class of a class that defines how a class behaves. A class is itself an instance of a metaclass. A class in Python defines how the instance of the class will behave. In order to understand metaclasses well, one needs to have prior experience working with Python classes.

Is metaclass inherited?

In this well known answer that explains metaclass in Python. It mentions that the __metaclass__ attribute will not be inherited. As expected, both Foo and Bar use Meta1 as metaclass and print string as expected.

Why would you use metaclasses?

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.

What is metaclass in OOP?

In object-oriented programming, a metaclass is a class whose instances are classes. Just as an ordinary class defines the behavior of certain objects, a metaclass defines the behavior of certain classes and their instances. Not all object-oriented programming languages support metaclasses.


1 Answers

You can change the metaclass after class creation the same way that you can change the class of an object, however you'd have a lot of issues. For starters, the initial metaclass needs to be different from type, the __init__ and __new__ of the new metaclass won't be called (though you can manually call __init__ or a method that performs __init__'s job).

Possibly the least troublesome way to change the metaclass is to recreate the class again from scratch:

B = MetaClass(B.__name__, B.__bases__, B.__dict__)

But if you insist on changing the metaclass dynamically, you first need to define B with a temporary custom metaclass:

class _TempMetaclass(type):
    pass

class B:
    __metaclass__ = _TempMetaclass # or: type('temp', (type, ), {})

Then you can define the metaclass like that:

class MetaClass(type):
    def __init__(cls, *a, **kw):
        super(MetaClass, cls).__init__(*a, **kw)
        cls._actual_init(*a, **kw)
    def _actual_init(cls, *a, **kw):
        # actual initialization goes here

And then do something like:

B.__class__ = MetaClass
MetaClass._actual_init(B, B.__name__, B.__bases__, B.__dict__)

You also need to make sure that all the initialization of the class is done _actual_init. You can also add a classmethod to the metaclass that changes the metaclass for you.

Both solutions have the slight shortcoming that B's bases would be limited - they need to be compatible with both the original and the new metaclass, but I guess that's not an issue in your case.

like image 51
Rosh Oxymoron Avatar answered Oct 02 '22 16:10

Rosh Oxymoron