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.
CodePudding user response:
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.
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.