Can a metaclass be called?

To catch the eyes:

I think the documentation may be wrong!

According to Python documentation 2.7.12, 3.4.3. Setting up class creationΒΆ :

__metaclass__ This variable can be any invoked accepting arguments for name, bases and dict. After creating the class, the called is used instead of the built-in type() .

New in version 2.2.

However, this article states:

Q : Wow! Is it possible to use an object of type __metaclass__ ?

A : No. It must be a subclass of the base object type ....

So, I did the experiment myself:

 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?

+5
source share
1 answer

No, any called calls will do. In your case, the type.__new__() method has a restriction that you violate; this has nothing to do with what you assign __metaclass__ .

The function is called:

 def metaclass_function(name, bases, body): return type(name, bases, body) 

This simply returns the result of type() (not type.__new__() ), but it is just callable. The class uses the return value. You really could return something:

 >>> class Foo(object): ... __metaclass__ = lambda *args: [] ... >>> Foo [] 

Here, the caller creates an instance of the list, so Foo bound to the list. Not very useful, but __metaclass__ just called to create something and something is used directly.

In your example, the first argument to type.__new__() not a type, but a call that fails. mcs bound to a list , not a (subclass) of type . type.__new__() you can set such restrictions.

Now, since the metaclass is still attached to the class object ( type(ClassObj) returns it), and it is used to allow searching for attributes of the class object (where the attribute is not available in the MRO class), it is usually a good idea to subclass it type , therefore that it gives you the correct implementation of things like __getattribute__ . It is for this reason that type.__new__() makes a restriction on what can be passed as the first argument; this is the first argument that type() attached to the returned class object.

+5
source

All Articles