pythonclassmetaclassdynamic-class-creation

When is the __prepare__ method of a metaclass excuted and what uses its return value?


PEP 3115 has the following example of using the __prepare__ method of a metaclass (print statements are mine):

# The custom dictionary
class member_table(dict):
    def __init__(self):
        self.member_names = []

    def __setitem__(self, key, value):
        print(f'in __setitem__{key, value}')
        # if the key is not already defined, add to the
        # list of keys.
        if key not in self:
            self.member_names.append(key)

        # Call superclass
        dict.__setitem__(self, key, value)

# The metaclass
class OrderedClass(type):

    # The prepare function
    @classmethod
    def __prepare__(metacls, name, bases): # No keywords in this case
        print('in __prepare__')
        return member_table()

    # The metaclass invocation
    def __new__(cls, name, bases, classdict):
        print('in __new__')
        # Note that we replace the classdict with a regular
        # dict before passing it to the superclass, so that we
        # don't continue to record member names after the class
        # has been created.
        result = type.__new__(cls, name, bases, dict(classdict))
        result.member_names = classdict.member_names
        return result

print('before MyClass')

class MyClass(metaclass=OrderedClass):
    print('in MyClass 1')

    # method1 goes in array element 0
    def method1(self):
        pass
    
    print('in MyClass 2')

    # method2 goes in array element 1
    def method2(self):
        pass
    
    print('in MyClass 3')

Running this, prints this:

before MyClass
in __prepare__
in __setitem__('__module__', '__main__')
in __setitem__('__qualname__', 'MyClass')
in MyClass 1
in __setitem__('method1', <function MyClass.method1 at 0x7fa70414da60>)
in MyClass 2
in __setitem__('method2', <function MyClass.method2 at 0x7fa70414daf0>)
in MyClass 3
in __new__

So it seems like when MyClass is executed, execution first goes to the class's metaclass's __prepare__ method which returns member_table() (who/what uses this return value?), then something sets the class's __module__ and __qualname__, then executes the class body, which sets the class's methods (method1 and method2), then the __new__ method is called with the return value of __prepare__ as the classdict argument value to __new__ (who/what is passing along this value?).


I tried to step through the execution in thonny's debugger, but that threw an error. I also tried stepping through execution in pythontutor.com, but that wasn't granular enough. I pdb'ed it, but it was hard to follow what was going on. Finally, I added some print statements, which are present in the code above.


Solution

  • The result of the prepare() is the namespace argument that gets passed to __new__. It is the namespace in which the body of the class is evaluated (see [1]).

    So within the newly created class, you can see the values of MyClass.__module__, MyClass.__qualname__, etc because they are being assigned in the namespace object of MyClass.

    Most uses of metaclasses have no need for prepare(), and an ordinary namespace is used.

    [1] https://docs.python.org/3.9/reference/datamodel.html?highlight=__prepare__#preparing-the-class-namespace