An operational explanation of Python metaclasses (part 3)
Following on from part 1 and part 2, the third thing that we can do with a metaclass is to create 'class-only attributes', attributes that are only visible on the class and not on instances of the class. In fact we can go further than just adding attributes; we can control what attributes are directly visible on the class without affecting what attributes are visible on instances of the class.
Simply adding attributes to the class (and only to the class) is done by putting attributes on the metaclass; in fact all attributes on the metaclass are visible on the class but not on instances of the class. Since this has some subtle bits, here is an example to illustrate:
class MiniMeta(type): one = "meta" two = "meta" class Alpha(object): two = "alpha" class Beta(Alpha): __metaclass__ = MiniMeta bi = Beta()
Beta.one is "meta", but
bi.one is an
AttributeError; the attribute
is visible only on the class and is not visible in instances.
bi.two are both "alpha"; the attribute on the parent class
overrides the attribute on the metaclass for both the class and an
instance of the class (the same thing happens if we define
Beta itself). Well, mostly.
The exception to parent classes taking priority is properties. A property set on the metaclass overrides anything else when you access it on the class, but is invisible to instances of the class. If you try hard this can be used to create attributes that have one value on the class and another value on instances of the class, which is sure to confuse everyone who reads your code unless you comment it heavily (and maybe even then).
The advanced version of this is that you can get partial or (almost)
full control of attribute access to the class itself by setting
__delattr__ special methods on the metaclass. When people access
attributes on the class itself (eg, as
Beta.attr), these work just
Beta was an ordinary instance of
MiniMeta (because that's
actually exactly what it is). However they are ignored when accessing
Beta through instances of it, eg as
__getattribute__ and so on are not even called.
(As with properties, this can be abused to create attributes which have a different value on the class than on instances of the class.)
Note that this not-looking happens even when the lookup on the class
is implicit, such as when you do
len(bi) and Python looks to see
if there's a
Beta.__len__ method. In fact special method lookups
don't go through any
__getattr__ at all
(this is covered in the official documentation).
(As with part 2, this is a specific example of a general metaclass power.)
Sidebar: method functions on the metaclass versus
Both of these create 'class methods', method functions that take the
class as the 'self' argument instead of an instance of the class.
However they are not quite the same thing; in particular,
methods are visible (and work) from instances of the class while
metaclass methods are not.
My personal opinion is that most of the time you want
because far more people are going to understand what you're doing. You
want this even if there's no common ancestor class you can put the
methods on and you have to invent some artificial mixin class to hold