元类可以任何可调用的吗?

时间:2016-07-28 15:55:32

标签: python python-2.7 class types metaclass

抓住你的眼睛:

我认为文档可能有误!

根据Python 2.7.12文档, 3.4.3。自定义课程创建

  

__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

文件真的错了吗?

1 个答案:

答案 0 :(得分: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
[]

这里callable生成了一个列表实例,因此Foo绑定到一个列表。不是很有用,但__metaclass__只是被调用来生成某些东西,并且直接使用了某些内容。

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

现在,因为元类仍然绑定到类对象(type(ClassObj)返回它),并且在解析类对象上的属性查找时使用它(其中属性在类MRO中不可用) ,它通常是一个鸣喇叭的好主意,使其成为type的子类,因为这为您提供了__getattribute__之类的正确实现。正是出于这个原因,type.__new__()限制了作为第一个参数传递的内容;它是type()附加到返回的类对象的第一个参数。