“MetaClass”, “__new__”, “cls” and “super” – what is the mechanism exactly?

OK, you’ve thrown quite a few concepts into the mix here! I’m going to pull out a few of the specific questions you have.

In general, understanding super, the MRO and metclasses is made much more complicated because there have been lots of changes in this tricky area over the last few versions of Python.

Python’s own documentation is a very good reference, and completely up to date. There is an IBM developerWorks article which is fine as an introduction and takes a more tutorial-based approach, but note that it’s five years old, and spends a lot of time talking about the older-style approaches to meta-classes.

super is how you access an object’s super-classes. It’s more complex than (for example) Java’s super keyword, mainly because of multiple inheritance in Python. As Super Considered Harmful explains, using super() can result in you implicitly using a chain of super-classes, the order of which is defined by the Method Resolution Order (MRO).

You can see the MRO for a class easily by invoking mro() on the class (not on an instance). Note that meta-classes are not in an object’s super-class hierarchy.

Thomas’ description of meta-classes here is excellent:

A metaclass is the class of a class.
Like a class defines how an instance
of the class behaves, a metaclass
defines how a class behaves. A class
is an instance of a metaclass.

In the examples you give, here’s what’s going on:

  1. The call to __new__ is being
    bubbled up to the next thing in the
    MRO. In this case, super(MyType, cls) would resolve to type;
    calling type.__new__ lets Python
    complete it’s normal instance
    creation steps.

  2. This example is using meta-classes
    to enforce a singleton. He’s
    overriding __call__ in the
    metaclass so that whenever a class
    instance is created, he intercepts
    that, and can bypass instance
    creation if there already is one
    (stored in cls.instance). Note
    that overriding __new__ in the
    metaclass won’t be good enough,
    because that’s only called when
    creating the class. Overriding
    __new__ on the class would work,
    however.

  3. This shows a way to dynamically
    create a class. Here’s he’s
    appending the supplied class’s name
    to the created class name, and
    adding it to the class hierarchy
    too.

I’m not exactly sure what sort of code example you’re looking for, but here’s a brief one showing meta-classes, inheritance and method resolution:

print('>>> # Defining classes:')

class MyMeta(type):
    def __new__(cls, name, bases, dct):
        print("meta: creating %s %s" % (name, bases))
        return type.__new__(cls, name, bases, dct)

    def meta_meth(cls):
        print("MyMeta.meta_meth")

    __repr__ = lambda c: c.__name__

class A(metaclass=MyMeta):
    def __init__(self):
        super(A, self).__init__()
        print("A init")

    def meth(self):
        print("A.meth")

class B(metaclass=MyMeta):
    def __init__(self):
        super(B, self).__init__()
        print("B init")

    def meth(self):
        print("B.meth")

class C(A, B, metaclass=MyMeta):
    def __init__(self):
        super(C, self).__init__()
        print("C init")

print('>>> c_obj = C()')
c_obj = C()
print('>>> c_obj.meth()')
c_obj.meth()
print('>>> C.meta_meth()')
C.meta_meth()
print('>>> c_obj.meta_meth()')
c_obj.meta_meth()

Example output (using Python >= 3.6):

>>> # Defining classes:
meta: creating A ()
meta: creating B ()
meta: creating C (A, B)
>>> c_obj = C()
B init
A init
C init
>>> c_obj.meth()
A.meth
>>> C.meta_meth()
MyMeta.meta_meth
>>> c_obj.meta_meth()
Traceback (most recent call last):
File "metatest.py", line 41, in <module>
    c_obj.meta_meth()
AttributeError: 'C' object has no attribute 'meta_meth'

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)