2011-09-17
An operational explanation of Python metaclasses (part 2)
After modifying a class as it's being created (covered in part 1), the next thing you can do with a metaclass is
get a chance to do things when instances of the class are created.
You do this by defining __call__
on your metaclass:
class MiniMeta(type): def __call__(cls, *args, **kwargs): return super(MiniMeta, cls).\ __call__(*args, **kwargs) class Example(object): __metaclass__ = MiniMeta
There are a number of things that you can do with this. One of them
(which I first saw in another metaclass tutorial) is handling deferred
initialization and setup of something related to the class. Rather than
doing this in your metaclass __new__
or __init__
, you defer it
until the first time an instance of the class is created; this saves
you effort in situations where many classes are defined but only a few
classes will ever be used to create instances.
Using a metaclass __call__
to customize instance creation runs into
the obvious question of why you don't just do the same work in the
class's own __init__
method (or in extreme cases, its __new__
method). Probably the right answer is if you have a chunk of common
behavior across a bunch of classes and the classes can't easily be put
into an inheritance relationship where this functionality can be pulled
into a common ancestor or mixin class.
(This use of metaclasses is considered sufficiently interested to
get mentioned in passing in the official documentation for
__metaclass__
.)
PS: __call__
is actually a specific example of a general metaclass
power. I'll get to the general power later because it requires more
explanation.
Sidebar: a technicality
Strictly speaking using __call__
does not intercept all instance
creation, since a sufficiently creative person can still obtain new
instances of Example
by calling object.__new__()
directly.
If you're doing something where you have to worry about this, Python
is probably the wrong language to write your code in.