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.
|
|