I have read answers for this question: What are metaclasses in Python? and this question: In Python, when should I use a meta class? and skimmed through documentation: Data model.
It is very possible I missed something, and I would like to clarify: is there anything that metaclasses can do that cannot be properly or improperly (unpythonic, etc) done with the help of other tools (decorators, inheritance, etc)?
That is a bit tricky to answer - However, it is a very nice question to ask at this point, and there are certainly a few things that are easier to do with metaclasses.
So, first, I think it is important to note the things for which one used to need a metaclass in the past, and no longer needs to: I'd say that with the release of Python 3.6 and the inclusion of
__init_subclass__and__set_name__dunder methods, a lot, maybe the majority of the cases I had always written a metaclass for (most of them for answering questions or in toy code - no one creates that many production-code metaclasses even in a lifetime as a programmer) became outdated.Specially
__init_subclass__adds the convenience of being able to transform any attribute or method like class-decorators, but is automatically applied on inheritance, which does not happen with decorators. I guess reading about it was a fator motivating your question - since most metaclasses found out in the wild deal with transforming these attributes in__new__and__init__metaclass methods.However, note that if one needs to transform any attribute prior to having it included in the class, the metaclass
__new__method is the only place it can be done. In most cases, however, one can simply transform it in the final new class namespace.Then, one version forward, in 3.7, we had
__class_getitem__implemented - since using the[ ](__getitem__) operator directly on classes became popular due to typing annotations. Before that, one would have to create a metaclass with a__getitem__method for the sole purpose of being able to indicate to the type-checker toolchain some extra information like generic variables.One interesting possibility that did not exist in Python 2, was introduced in Python 3, then outdated, and now can only serve very specific cases is the use of the
__prepare__method on the metaclass: I don't know if this is written in any official docs, but the obvious primary motivation for metaclass__prepare__which allows one custom namespace for the class body, was to return an ordered dict, so that one could have ordered attributes in classes that would work as data entities. It turns out that also, from Python 3.6 on, class body namespaces where always ordered (which later on Python 3.7 were formalized for all Python dictionaries). However, although not needed for returning anOrderedDictanymore,__prepare__is still aunique thing in the language in which it allows a custom mapping class to be used as namespace in a piece of Python code (even if that is limited to class bodies). For example, one can trivialy create an "auto-enumeration" metaclass by returning a(an example similar to this is included in Luciano Ramalho's 'Fluent Python' 2nd edition)
The
__call__method on the metaclass is also peculiar: it control the calls to__new__and__init__whenever an instance of the class is created. There are recipes around that use this to create a "singleton" - I find those terrible and overkill: if I need a singleton, I just create an instance of the singleton class at module level. However, overridingtyping.__call__offers a level of control on class instantiation that may be hard to achieve on the class__new__and__init__themselves. But this definitely can be done by correctly keeping the desired states in the class object itself.__subclasscheck__and__instancecheck__: these are metaclass only methods, and the only workaround would be to make a class decorator that would re-create a class object so that it would be a "real" subclass of the intended base class. (and that is not always possible)."hidden" class attributes: now, this can be useful, and is less known, as it derives from the language behavior itself: any attribute or method besides the
dundermethods included in a metaclass can be used from a class, but from instances of that class. An example for this is the.registermethod in classes usingabc.ABCMeta. This contrasts with ordinary classmethods which can be used normally from an instance.And finally, any behavior defined with the dunder methods for a Python object can be implemented to work on classes if they are defined in the metaclass. So if you have any use case for "add-able" classes, or want a special
reprfor your classes, just implement__add__or__repr__on the metaclass: this behavior obviously can't be obtained by other means.I think I got all covered there.