A head first guide to metaclasses in Python. The presentation will show you how metaclasses work, how they affect attribute resolution and few potential usecases for them. At the end you'll have a pretty good idea of how Django models and other metaclass (ab)using libraries work.
HTML5 version: http://blog.ionelmc.ro/presentations/metaclase/
Presented at: http://conference.ropython.org/
9. But there are some caveats
Magic methods are looked up (resolved) on the class, not on the instance. [1]
>>> class Simple:
... pass
>>> s = Simple()
Doesnʹt work:
>>> s.__call__ = lambda self: "x"
>>> s()
Traceback (most recent call last):
...
TypeError: 'Simple' object is not callable
Aha! It is resolved on the class:
>>> Simple.__call__ = lambda self: "x"
>>> s()
'x'
[1] Exceptions: Python 2 old‑style objects and few special‑cased methods.
15. You can have a custom metaclass
So, if a class is just an instance, we can customise the creation process:
>>> class Meta(type):
... pass
>>> class Complex(metaclass=Meta):
... pass
>>> Complex()
<__main__.Complex object at 0x...>
Normally, the metaclass should be a subclass of type . More on this later.
16.
17. The instantiation dance in detail
Remember that:
__init__ does not create instances, __new__ does.
Magic methods are resolved on the metaclass. Complex() is equivalent to
Meta.__call__ .
18.
19. The full interface
__prepare__(mcs, name, bases, **kwargs) ‑ New in Python 3, returns the
namespace dictionary
__new__(mcs, name, bases, attrs, **kwargs) ‑ Returns an instance of
Meta
__init__(cls, name, bases, attrs, **kwargs) ‑ Runs initialisation code,
typeʹs __init__ doesnʹt do anything
kwargs: only in Python 3, example:
class Class(object, metaclass=Meta, a=1, b=2, c=3):
pass
__call__ ‑ Returns and instance of Complex (which in turn is instance of Meta).
Because magic methods are resolved on the class.
23. Going back to the type of the metaclass
Normally you inherit from type because it implements all the necessary interface
for a well functioning class.
But you can use just as well a plain callable.
And let horrible stuff like this happen:
>>> class NotAnymore(metaclass=print):
... pass
NotAnymore () {'__qualname__': 'NotAnymore', '__module__': '__main__'}
>>> repr(NotAnymore)
'None'
31. New in Python 3: __prepare__
Allows users to customize the class creation before the body of the class is
executed.
Basically allows you to return a different object as the namespace (instead of a
dict).
What can you do with it? Lots of interesting stuff:
Single dispatch
Duplicate validators
Field order aware objects
32.
33. Disallow overrides with __prepare__
>>> class StrictDict(dict):
... def __setitem__(self, name, value):
... if name in self:
... raise RuntimeError('You already defined %r!' % name)
... super().__setitem__(name, value)
...
>>> class StrictMeta(type):
... def __prepare__(name, bases):
... return StrictDict()
...
>>> class Strict(metaclass=StrictMeta):
... a = 1
... a = 2 # Ooops. Will ever anyone notice this?
Traceback (most recent call last):
...
RuntimeError: You already defined 'a'!
34.
35. Single dispatch with __prepare__ ﴾1/3﴿
Suppose we have this code:
>>> class Int(int):
... def __repr__(self):
... return "Int(%d)" % self
...
... @dispatching.dispatch
... def __add__(self, other:int):
... return Int(int.__add__(self, other))
...
... @__add__.dispatch
... def __add__(self, other:str):
... return Int(int.__add__(self, int(other)))
>>> i = Int(5)
>>> i + 1
Int(6)
>>> i + "2"
Int(7)
36.
37. Single dispatch with __prepare__ ﴾2/3﴿
We can make it seamless using __prepare__ :
>>> class Int(int, metaclass=SingleDispatchMeta):
... def __repr__(self):
... return "Int(%d)" % self
...
... def __add__(self, other:int):
... return Int(int.__add__(self, other))
...
... def __add__(self, other:str):
... return Int(int.__add__(self, int(other)))
>>> i = Int(5)
>>> i + 1
Int(6)
>>> i + "2"
Int(7)
38.
39. Single dispatch with __prepare__ ﴾3/3﴿
>>> import dispatching
>>> class SingleDispatchCollector(dict):
... def __setitem__(self, name, value):
... if callable(value):
... if name in self:
... self[name].dispatch(value)
... else:
... super().__setitem__(name,
... dispatching.dispatch(value))
... else:
... super().__setitem__(name, value)
>>> class SingleDispatchMeta(type):
... def __prepare__(name, bases):
... return SingleDispatchCollector()
40.
41. What else can you do with metaclasses
Docstring fixers
DSLs, Models
Class or usage validators
Subclass registry systems
All sorts of behavior changing
Warning/deprecation systems
Automatic function decoration
42.
43. Metaclasses vs class decorators
A class decorator takes a class as input and, hopefully, returns a class.
The decorator usually returns the same class after making some changes to it (eg:
monkey‑patching).
Disadvantages of a decorator:
Lack of flexibility. They are equivalent to a __init__ method in the
metaclass.
When accessing methods through the class or instance they become bound
functions (which is not the same thing as the original function).
Solution is to pull them out of __dict__ .
It becomes boilerplate.
Doesnʹt work well with subclassing.
47. Where do we draw the line?
Metaclasses have some disadvantages. What is a good tradeoff?
Whatʹs worse, boilerplate or implicit behavior?
Two important things to consider:
Is the implicit behavior intuitive?
Does the abstraction leak?