I have a friend who likes to use metaclasses, and regularly offers them as a solution.
I am of the mind that you almost never need to use metaclasses. Why? because I figure if you are doing something like that to a class, you should probably be doing it to an object. And a small redesign/refactor is in order.
Being able to use metaclasses has caused a lot of people in a lot of places to use classes as some kind of second rate object, which just seems disastrous to me. Is programming to be replaced by meta-programming? The addition of class decorators has unfortunately made it even more acceptable.
So please, I am desperate to know your valid (concrete) use-cases for metaclasses in Python. Or to be enlightened as to why mutating classes is better than mutating objects, sometimes.
I will start:
Sometimes when using a third-party
library it is useful to be able to
mutate the class in a certain way.
I have a class that handles non-interactive plotting, as a frontend to Matplotlib. However, on occasion one wants to do interactive plotting. With only a couple functions I found that I was able to increment the figure count, call draw manually, etc, but I needed to do these before and after every plotting call. So to create both an interactive plotting wrapper and an offscreen plotting wrapper, I found it was more efficient to do this via metaclasses, wrapping the appropriate methods, than to do something like:
class PlottingInteractive: add_slice = wrap_pylab_newplot(add_slice)
This method doesn't keep up with API changes and so on, but one that iterates over the class attributes in
__init__ before re-setting the class attributes is more efficient and keeps things up to date:
class _Interactify(type): def __init__(cls, name, bases, d): super(_Interactify, cls).__init__(name, bases, d) for base in bases: for attrname in dir(base): if attrname in d: continue # If overridden, don't reset attr = getattr(cls, attrname) if type(attr) == types.MethodType: if attrname.startswith("add_"): setattr(cls, attrname, wrap_pylab_newplot(attr)) elif attrname.startswith("set_"): setattr(cls, attrname, wrap_pylab_show(attr))
Of course, there might be better ways to do this, but I've found this to be effective. Of course, this could also be done in
__init__, but this was the solution I found the most straightforward.