2016-07-28 46 views
5

为了吸引您的眼球:元类可以被调用吗?

我认为文档可能是错的!

根据Python 2.7.12文档,3.4.3。定制类creation¶

__metaclass__此变量可以是任何调用接受 论据名,碱和字典。在创建课程时,使用可调用的 代替内置的type()

版本2.2中的新功能。

然而,this article认为:

Q:哇!我可以使用任何类型的对象作为__metaclass__吗?

A:否。它必须是基础对象类型的子类。 ...

所以,我做我自己的一个实验:

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 

这给了我:

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 

所以是文档真的错了吗?

回答

5

不,任何可调用的都可以。在你的情况下,type.__new__()方法有你违反的限制;这与您分配给__metaclass__的内容无关。

函数是可以调用的:

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

这一个刚刚返回的type()(不type.__new__())的结果,但它是只是调用。返回值使用作为类别。你真的可以返回任何东西:

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

这里可调用产生的列表实例,所以Foo绑定到一个列表。不是很有用,但__metaclass__只是调用产生东西,而且直接使用。

在您的例子中,第一个参数是type.__new__(),这是调用失败。 mcs绑定到list,而不是(的子类)。 type.__new__()可自由设置此类限制。

现在,因为元类仍然与类对象绑定(type(ClassObj)返回它),并且它在解析类对象(其中属性在类MRO中不可用)的属性查找时使用,所以它是通常是一个鸣响好主意,使其成为type的一个子类别,因为这可以让您正确实施诸如__getattribute__之类的内容。正是由于这个原因,type.__new__()限制了第一个参数可以传入的内容;这是type()附加到返回的类对象的第一个参数。

+0

“绑定到”的意思是“一个子类”,对吧? –

+0

@sunqingyao:不,绑定表示分配给。赋值'Foo = []'被执行。一个'class'语句基本上被视为一个函数;正如函数中一样执行主体,并将所有本地名称提取到字典中。然后通过调用'bodydict.get('__ metaclass__',type)'(所以'__metaclass__'的值或者如果缺少'type'')并传入类名称,类的基类,以及身体字典。 *无论返回*分配给班级的名称。绑定是技术术语;其他的东西也绑定了,比如'import'或'for'。 –

+0

但是通过说“mcs'绑定到'list'”,你似乎意思是“mcs'是'list'的子类的一个实例。 –

相关问题