Categories
metaclass oop python python-class python-datamodel

What are metaclasses in Python?

6873

What are metaclasses? What are they used for?

0

    3369

    A metaclass is the class of a class. A class defines how an instance of the class (i.e. an object) behaves while a metaclass defines how a class behaves. A class is an instance of a metaclass.

    While in Python you can use arbitrary callables for metaclasses (like Jerub shows), the better approach is to make it an actual class itself. type is the usual metaclass in Python. type is itself a class, and it is its own type. You won’t be able to recreate something like type purely in Python, but Python cheats a little. 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. Combined with the normal __init__ and __new__ methods, metaclasses therefore allow you to do ‘extra things’ when creating a class, like registering the new class with some registry or replace the class with something else entirely.

    When the class statement is executed, Python first executes the body of the class statement as a normal block of code. The resulting namespace (a dict) holds the attributes of the class-to-be. The metaclass is determined by looking at the baseclasses of the class-to-be (metaclasses are inherited), at the __metaclass__ attribute of the class-to-be (if any) or the __metaclass__ global variable. The metaclass is then called with the name, bases and attributes of the class to instantiate it.

    However, metaclasses actually define the type of a class, not just a factory for it, so you can do much more with them. You can, for instance, define normal methods on the metaclass. These metaclass-methods are like classmethods in that they can be called on the class without an instance, but they are also not like classmethods in that they cannot be called on an instance of the class. type.__subclasses__() is an example of a method on the type metaclass. You can also define the normal ‘magic’ methods, like __add__, __iter__ and __getattr__, to implement or change how the class behaves.

    Here’s an aggregated example of the bits and pieces:

    def make_hook(f):
        """Decorator to turn 'foo' method into '__foo__'"""
        f.is_hook = 1
        return f
    
    class MyType(type):
        def __new__(mcls, name, bases, attrs):
    
            if name.startswith('None'):
                return None
    
            # Go over attributes and see if they should be renamed.
            newattrs = {}
            for attrname, attrvalue in attrs.iteritems():
                if getattr(attrvalue, 'is_hook', 0):
                    newattrs['__%s__' % attrname] = attrvalue
                else:
                    newattrs[attrname] = attrvalue
    
            return super(MyType, mcls).__new__(mcls, name, bases, newattrs)
    
        def __init__(self, name, bases, attrs):
            super(MyType, self).__init__(name, bases, attrs)
    
            # classregistry.register(self, self.interfaces)
            print "Would register class %s now." % self
    
        def __add__(self, other):
            class AutoClass(self, other):
                pass
            return AutoClass
            # Alternatively, to autogenerate the classname as well as the class:
            # return type(self.__name__ + other.__name__, (self, other), {})
    
        def unregister(self):
            # classregistry.unregister(self)
            print "Would unregister class %s now." % self
    
    class MyObject:
        __metaclass__ = MyType
    
    
    class NoneSample(MyObject):
        pass
    
    # Will print "NoneType None"
    print type(NoneSample), repr(NoneSample)
    
    class Example(MyObject):
        def __init__(self, value):
            self.value = value
        @make_hook
        def add(self, other):
            return self.__class__(self.value + other.value)
    
    # Will unregister the class
    Example.unregister()
    
    inst = Example(10)
    # Will fail with an AttributeError
    #inst.unregister()
    
    print inst + inst
    class Sibling(MyObject):
        pass
    
    ExampleSibling = Example + Sibling
    # ExampleSibling is now a subclass of both Example and Sibling (with no
    # content of its own) although it will believe it's called 'AutoClass'
    print ExampleSibling
    print ExampleSibling.__mro__
    

    10

    • 18

      class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b

      – pppery

      Aug 3, 2017 at 14:34

    • 34

      ppperry he obviously meant you can’t recreate type without using type itself as a metaclass. Which is fair enough to say.

      – Holle van

      Sep 18, 2018 at 23:24

    • 5

      Shouldn’t unregister() be called by instance of Example class ?

      Nov 29, 2018 at 0:59

    • 20

      Note that __metaclass__ is not supported in Python 3. In Python 3 use class MyObject(metaclass=MyType), see python.org/dev/peps/pep-3115 and the answer below.

      May 1, 2019 at 8:36

    • 5

      The documentation describes how the metaclass is chosen. The metaclass isn’t inherited so much as it is derived. If you specify a metaclass, it has to be a subtype of each base class metaclass; otherwise, you’ll use the a base class metaclass that is a subtype of each other base class metaclass. Note that it is possible that no valid metaclass can be found, and the definition will fail.

      – chepner

      Jan 9, 2020 at 21:14

    3369

    A metaclass is the class of a class. A class defines how an instance of the class (i.e. an object) behaves while a metaclass defines how a class behaves. A class is an instance of a metaclass.

    While in Python you can use arbitrary callables for metaclasses (like Jerub shows), the better approach is to make it an actual class itself. type is the usual metaclass in Python. type is itself a class, and it is its own type. You won’t be able to recreate something like type purely in Python, but Python cheats a little. 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. Combined with the normal __init__ and __new__ methods, metaclasses therefore allow you to do ‘extra things’ when creating a class, like registering the new class with some registry or replace the class with something else entirely.

    When the class statement is executed, Python first executes the body of the class statement as a normal block of code. The resulting namespace (a dict) holds the attributes of the class-to-be. The metaclass is determined by looking at the baseclasses of the class-to-be (metaclasses are inherited), at the __metaclass__ attribute of the class-to-be (if any) or the __metaclass__ global variable. The metaclass is then called with the name, bases and attributes of the class to instantiate it.

    However, metaclasses actually define the type of a class, not just a factory for it, so you can do much more with them. You can, for instance, define normal methods on the metaclass. These metaclass-methods are like classmethods in that they can be called on the class without an instance, but they are also not like classmethods in that they cannot be called on an instance of the class. type.__subclasses__() is an example of a method on the type metaclass. You can also define the normal ‘magic’ methods, like __add__, __iter__ and __getattr__, to implement or change how the class behaves.

    Here’s an aggregated example of the bits and pieces:

    def make_hook(f):
        """Decorator to turn 'foo' method into '__foo__'"""
        f.is_hook = 1
        return f
    
    class MyType(type):
        def __new__(mcls, name, bases, attrs):
    
            if name.startswith('None'):
                return None
    
            # Go over attributes and see if they should be renamed.
            newattrs = {}
            for attrname, attrvalue in attrs.iteritems():
                if getattr(attrvalue, 'is_hook', 0):
                    newattrs['__%s__' % attrname] = attrvalue
                else:
                    newattrs[attrname] = attrvalue
    
            return super(MyType, mcls).__new__(mcls, name, bases, newattrs)
    
        def __init__(self, name, bases, attrs):
            super(MyType, self).__init__(name, bases, attrs)
    
            # classregistry.register(self, self.interfaces)
            print "Would register class %s now." % self
    
        def __add__(self, other):
            class AutoClass(self, other):
                pass
            return AutoClass
            # Alternatively, to autogenerate the classname as well as the class:
            # return type(self.__name__ + other.__name__, (self, other), {})
    
        def unregister(self):
            # classregistry.unregister(self)
            print "Would unregister class %s now." % self
    
    class MyObject:
        __metaclass__ = MyType
    
    
    class NoneSample(MyObject):
        pass
    
    # Will print "NoneType None"
    print type(NoneSample), repr(NoneSample)
    
    class Example(MyObject):
        def __init__(self, value):
            self.value = value
        @make_hook
        def add(self, other):
            return self.__class__(self.value + other.value)
    
    # Will unregister the class
    Example.unregister()
    
    inst = Example(10)
    # Will fail with an AttributeError
    #inst.unregister()
    
    print inst + inst
    class Sibling(MyObject):
        pass
    
    ExampleSibling = Example + Sibling
    # ExampleSibling is now a subclass of both Example and Sibling (with no
    # content of its own) although it will believe it's called 'AutoClass'
    print ExampleSibling
    print ExampleSibling.__mro__
    

    10

    • 18

      class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b

      – pppery

      Aug 3, 2017 at 14:34

    • 34

      ppperry he obviously meant you can’t recreate type without using type itself as a metaclass. Which is fair enough to say.

      – Holle van

      Sep 18, 2018 at 23:24

    • 5

      Shouldn’t unregister() be called by instance of Example class ?

      Nov 29, 2018 at 0:59

    • 20

      Note that __metaclass__ is not supported in Python 3. In Python 3 use class MyObject(metaclass=MyType), see python.org/dev/peps/pep-3115 and the answer below.

      May 1, 2019 at 8:36

    • 5

      The documentation describes how the metaclass is chosen. The metaclass isn’t inherited so much as it is derived. If you specify a metaclass, it has to be a subtype of each base class metaclass; otherwise, you’ll use the a base class metaclass that is a subtype of each other base class metaclass. Note that it is possible that no valid metaclass can be found, and the definition will fail.

      – chepner

      Jan 9, 2020 at 21:14

    473

    Note, this answer is for Python 2.x as it was written in 2008, metaclasses are slightly different in 3.x.

    Metaclasses are the secret sauce that make ‘class’ work. The default metaclass for a new style object is called ‘type’.

    class type(object)
      |  type(object) -> the object's type
      |  type(name, bases, dict) -> a new type
    

    Metaclasses take 3 args. ‘name‘, ‘bases‘ and ‘dict

    Here is where the secret starts. Look for where name, bases and the dict come from in this example class definition.

    class ThisIsTheName(Bases, Are, Here):
        All_the_code_here
        def doesIs(create, a):
            dict
    

    Lets define a metaclass that will demonstrate how ‘class:‘ calls it.

    def test_metaclass(name, bases, dict):
        print 'The Class Name is', name
        print 'The Class Bases are', bases
        print 'The dict has', len(dict), 'elems, the keys are', dict.keys()
    
        return "yellow"
    
    class TestName(object, None, int, 1):
        __metaclass__ = test_metaclass
        foo = 1
        def baz(self, arr):
            pass
    
    print 'TestName=", repr(TestName)
    
    # output => 
    The Class Name is TestName
    The Class Bases are (<type "object'>, None, <type 'int'>, 1)
    The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
    TestName="yellow"
    

    And now, an example that actually means something, this will automatically make the variables in the list “attributes” set on the class, and set to None.

    def init_attributes(name, bases, dict):
        if 'attributes' in dict:
            for attr in dict['attributes']:
                dict[attr] = None
    
        return type(name, bases, dict)
    
    class Initialised(object):
        __metaclass__ = init_attributes
        attributes = ['foo', 'bar', 'baz']
    
    print 'foo =>', Initialised.foo
    # output=>
    foo => None
    

    Note that the magic behaviour that Initialised gains by having the metaclass init_attributes is not passed onto a subclass of Initialised.

    Here is an even more concrete example, showing how you can subclass ‘type’ to make a metaclass that performs an action when the class is created. This is quite tricky:

    class MetaSingleton(type):
        instance = None
        def __call__(cls, *args, **kw):
            if cls.instance is None:
                cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
            return cls.instance
    
    class Foo(object):
        __metaclass__ = MetaSingleton
    
    a = Foo()
    b = Foo()
    assert a is b
    

    0