Improve Your Python: Metaclasses and Dynamic Classes With Type
jeffknupp.com
jeffknupp.com
This is probably my all-time favorite StackOverflow post!
The example at the end is perfectly well written using the class statement and using register as a class decorator, while being more familiar and readable.
It gets tiring to hear people say "oh advanced Python? Like metaclasses, I'll learn that".
Learn useful things instead, like writing readable, testable code.
So, I think this is a pretty reasonable place to use them -- getting magic behavior from class decorators / metaclasses is bad enough, but getting surprised when losing it upon subclassing is even worse!
Metaclasses (and build_class) is a mechanism by which to enforce a constraint from a base type to a derived type.
(Note that, in practice, there is some trickiness around metaclasses on derived classes: http://seriously.dontusethiscode.com/2013/04/18/derived-meta...)
It's trivial to enforce a constraint from a derived type to a base type. e.g.,
# base.py
class Base:
def spam(self): pass
# derived.py
class Derived(Base):
assert hasattr(Base, 'spam') # or abc, &c.
def ham(self):
return self.spam
But how can we enforce a constraint in the other direction? (e.g., abc.ABCMeta) # base.py
from functools import wraps
class metaclass(type):
def __new__(m, n, b, d):
assert 'spam' in d # must implement, not just inherit
# can even enforce behaviour via wrapping
spam = d['spam']
@wraps(spam)
def wrap(*args, **kwargs):
print('wrap({}, {})'.format(args, kwargs))
return spam(*args, **kwargs)
d['spam'] = wrap
return type.__new__(m, n, b, d)
class Base(metaclass=metaclass):
def spam(self): pass
# derived.py
class Derived(Base):
def spam(self): passNo, it can't, because the name of the class is dynamically determined at run time. The class statement can't take a variable for the name of the class. That's the point.
Sure enough, when I'm done with my explanation, the participants agree that metaclasses are really something that they're not planning to touch or use. And then I repeat my claim (which isn't original with me at all) that decorators are easier to understand and maintain, and should be used instead.
I totally agree that learning to write readable, testable code is a far better use of time. (I include that in my classes, too... but people rarely request that, I'm afraid...)
i only know about that part of the django implementation because i was trying to set up some kind of class inheritance and found that things were behaving strangely. so the conclusion in this case was that the use of metaclass allowed some cleverness in the implementation of ModelForm, but it made it difficult to layer more cleverness on top of ModelForm.
It's a quick read and seems to be a nice shortcut to avoid a few newbie pitfalls (I'm a Python newbie myself).