|
This "infectious behaviour" (though perhaps unfairly characterised) leads to the clearest purpose of metaclasses. 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): pass
|