== Fake versus real metaclasses and what a fully functional metaclass is Lately I've become a little bit obsessed with the question of whether you can create a fully functional metaclass that doesn't inherit from _type_ (partly this was sparked by [[an @eevee tweet https://twitter.com/eevee/status/421479446730665984]], although it's an issue [[I brushed against a while back ClassesAndIsinstance]]). It's not so much that I want to do this or think that it's sensible as that I can't prove what the answer is either way and that bugs me. But before I try to tackle the big issues I want to talk about what I mean by 'fully functional metaclass'. Let's start with some very simple metaclasses, one of which inherits from _type_ and one of which doesn't: .pn prewrap on > class M1(type): > def __new__(self, name, bases, dct): > print "M1", name > return super(M1, self).__new__(self, name, bases, dct) > > class M2(object): > def __new__(self, name, bases, dct): > print "M2", name > return type(name, bases, dct) > > class C1(object): > __metaclass__ = M1 > > class C2(object): > __metaclass__ = M2 _M2_ certainly looks like a metaclass despite not inheriting from _type_ (eg if you try this out you can see that it is triggered on the creation of _C2_). But appearances are deceiving. _M2_ is not a fully functional metaclass (and there are ways to demonstrate this). So let me show you what's really going on: > >>> type(C1) > > >>> type(C2) > (We can get the same information by looking at each class's ((__class__)) attribute.) The type of a class with a metaclass is the metaclass while the type of a class without a metaclass is _type_, and as we can see from this, _C2_ doesn't actually have a metaclass. The reason for this is that _M2_ created the actual class object for _C2_ by calling _type()_ directly, which does not give the newly created class a metaclass (instead it becomes a direct instance of _type_). If all you're interested in is [[changing a class as it's being created UsingMetaclass01]] this may not matter, or at least you may not notice any side effects if you don't subclass your equivalent of _C2_. In this example _M1_ is what I call a fully functional metaclass and _M2_ is not. It looks like one and partly acts like one, but that is an illusion; at best it can do only one of [[the many things metaclasses can do MetaclassIndex]]. A fully functional metaclass like _M1_ can do all of them. Now let's come back to a demonstration that _M2_ is not a real metaclass. The most alarming way to demonstrate this is to subclass both classes: > class C3(C1): > pass > class C4(C2): > pass If you try this out you'll see that _M1_ is triggered when _C3_ is created but _M2_ is not triggered when _C4_ is created. This is very confusing because _C4_ (and _C2_ for that matter) has a visible ((__metaclass__)) attribute. It's just not meaningful after the creation of _C2_, contrary to what some documentation sometimes says. Note that this is sort of documented if you read [[Customizing class creation http://docs.python.org/2/reference/datamodel.html#customizing-class-creation]] very carefully; see the section on precedence rules, which only talks about looking at a ((__metaclass__)) attribute in *the actual class dictionary*, not the class dictionaries of any base classes. Note that this means that ~~general callables cannot be true metaclasses~~. To create a true metaclass, one that will be inherited by subclasses, you must arrange for the created classes to be instances of you, and only classes can have instances. If you have a ((__metaclass__)) of, say, a function, it will be called only when classes explicitly list it as their metaclass; it will not be called for subclasses. This is going to surprise everyone except experts in Python arcana, so don't do that even if you think you have a use for it. (If you do want to customize only classes that explicitly specify a ((__metaclass__)) attribute, do this check in your ((__new__)) function by looking at the passed in dictionary. Then people who read the code of your metaclass have a chance of working out what's going on.) I will admit that Python 3 cleaned this up by removing the magic ((__metaclass__)) attribute. Now you can't be misled quite as much by the presence of ((C2.__metaclass__)) and the visibility of ((C4.__metaclass__)). To determine whether something really has a metaclass in Python 3 you have no choice but to look at _type()_, which is always honest.