To catch your eyes:
According to Python 2.7.12 documentation, 3.4.3. Customizing class creation:
__metaclass__
This variable can be any callable accepting arguments for name, bases, and dict. Upon class creation, the callable is used instead of the built-intype()
.New in version 2.2.
However, this article argues:
Q: Wow! Can I use any type object as the
__metaclass__
?A: No. It must be a subclass of the type of the base object. ...
So I did an experiment on my own:
class metacls(list): # <--- subclassing list, rather than type
def __new__(mcs, name, bases, dict):
dict['foo'] = 'metacls was here'
return type.__new__(mcs, name, bases, dict)
class cls(object):
__metaclass__ = metacls
pass
This gives me:
Traceback (most recent call last):
File "test.py", line 6, in <module>
class cls(object):
File "test.py", line 4, in __new__
return type.__new__(mcs, name, bases, dict)
TypeError: Error when calling the metaclass bases
type.__new__(metacls): metacls is not a subtype of type
So is the document really wrong?
No, any callable will do. In your case the type.__new__()
method has a restriction you are violating; this has nothing to do with what you assign to __metaclass__
.
A function is a callable:
def metaclass_function(name, bases, body):
return type(name, bases, body)
This one just returns the result of type()
(not type.__new__()
), yet it is just a callable. The return value is used as the class. You could really return anything:
>>> class Foo(object):
... __metaclass__ = lambda *args: []
...
>>> Foo
[]
Here the callable produced a list instance, so Foo
is bound to a list. Not very useful, but __metaclass__
is just called to produce something, and that something is used directly.
In your example, the first argument to type.__new__()
is not a type, and it is that call that fails. mcs
is bound to list
, and not a (subclass of) type
. type.__new__()
is free to set such restrictions.
Now, because a metaclass is still tied to the class object (type(ClassObj)
returns it) and it is used when resolving attribute look-ups on the class object (where the attribute is not available in the class MRO), it is usually a honking good idea to make it a subclass of type
, because that gives you the right implementation of things like __getattribute__
. It is for that reason that type.__new__()
make a restriction on what can be passed in as the first argument; it is that first argument that type()
attaches to the class object returned.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With