为什么元类应该继承自type?

7
我们可以使用函数作为元类,我的理解是它们不像下面的那样派生自type:
def test_meta(name, bases, atts):
    print("testmeta called for " + name)
    return type(name,bases,atts);

class Computer:
    __metaclass__ = test_meta
    def __init__(self,brand,model,price):
        self.brand = brand
        self.model = model
        self.price = price

#
#
ob = Computer('p1','core2duo',21985.25)

然而,当我们编写元类时,它应该从type继承,我无法理解背后的原因:

class MyMeta:
    def __new__(meta, name, bases, dct):
        print ('-----------------------------------')
        print ("Allocating memory for class", name)
        print (meta)
        print (bases)
        print (dct)
        return type.__new__(meta, name, bases, dct)

    def __init__(cls, name, bases, dct):
        print ('-----------------------------------')
        print ("Initializing class", name)
        print (cls)
        print (bases)
        print (dct)
        type.__init__(cls,name, bases, dct)

    def __call__(mcs, *args, **kwargs):
        print ('-----------------------------------')
        print ("calling class")
        print mcs

class Computer:
    __metaclass__ = MyMeta
    def __init__(self,brand,model,price):
        self.brand = brand
        self.model = model
        self.price = price

#
#
ob = Computer('p1','core2duo',21985.25)

例如,在上面的代码中,我无法理解为什么MyMeta类应该从type继承,当我们明确调用newinitcall函数时。同时,“描述符'init'需要一个'type'对象但收到了一个'instance'”这个错误只有在创建i=instance (ob)时才会发生。

打印(type(ob)) -> NoneType。你刚刚在类型机制中损坏了某些东西。 - Moses Koledoye
1个回答

6

在普通的Python代码中,实际上只有一个调用可以创建一个内存中的类对象,具有二进制结构和所有必需字段,那就是type.__new__。如果将函数作为可调用的元类,那么它将只能实例化type本身,或者创建根本不是类的东西(见下文)。

可能会使用本机C代码或纯Python创建另一个“基本元类”,调用本机操作系统内存分配函数,并填充与“类型对象”定义相同的结构。然而,这只能实现type.__new__已经完成的任务,因此这只是一种复杂且容易出错的重新发明轮子,无法进一步改进,因为结果的二进制布局必须与该结构中定义的相同(即实现“魔术函数”的方法的指针,如__init____geitem__等,必须位于该结构中的相同偏移量处)

(通过继承type的代码可以实现此结构中的其他字段,甚至是数据的精美值 - 因此由普通元类完成)

正如您所说,任何可调用对象 - 甚至是简单函数,都可以在类主体中指定为“元类”(在Python 2和Python 3中均可) - 然而,无论这个可调用对象做什么,在某个时刻它都必须调用type.__new__(函数通过间接调用type来实现)。然而,一旦类被构建,它本身就是一个Python对象,它是一个类的实例 - 类的类型(在其__class__属性中)是其有效的元类。如果代码中指定的元类作为__metaclass__属性或Python 3中的元类kwarg的子类,则它也将成为元类适当的选择。否则,如果它只是一个普通的函数,只是作为一个在其主体中调用type的类工厂,那么有效的元类就是type。

换句话说:

In [1]: def function_meta(name, bases, ns):
   ...:     return type(name, bases, ns)
   ...: 

In [2]: class test(metaclass=function_meta):
   ...:     pass
   ...: 

In [3]: type(test)
Out[3]: type

In [4]: class ClassMeta(type):
   ...:     pass
   ...: 
   ...: 

In [5]: class test2(metaclass=ClassMeta):
   ...:     pass
   ...:

In [6]: type(test2)
Out[6]: __main__.ClassMeta

那么,为什么不能使用不继承type的类呢?

问题并不是指定的元类没有继承自type - 任何可调用对象都可以被使用,如上所示。 当您试图这样做时收到的错误是由于使用非type子类作为第一个参数调用type.__new__引起的:

In [11]: class InheritFromOther(object):
    ...:     def __new__(mcls, name, bases, ns):
    ...:         return type.__new__(mcls, name, bases, ns)
    ...:         # the line above is the one that errors - as
    ...:         # mcls here is not a subclass of type.
    ...:         # type(name, bases, ns) would work.

In [12]: class test4(metaclass=InheritFromOther):
    ...:     pass
    ...: 
    ...: 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-12-bf5b1fa4bb7f> in <module>()
----> 1 class test4(metaclass=InheritFromOther):
      2     pass

<ipython-input-11-62d1fe46490b> in __new__(mcls, name, bases, ns)
      1 class InheritFromOther(object):
      2     def __new__(mcls, name, bases, ns):
----> 3         return type.__new__(mcls, name, bases, ns)
      4 

TypeError: type.__new__(InheritFromOther): InheritFromOther is not a subtype of type

现在,如果我们使用一个有效的type子类作为第一个参数调用type.__new__:
In [13]: class InheritFromOtherTake2(object):
    ...:     def __new__(mcls, name, bases, ns):
    ...:         return type.__new__(type, name, bases, ns)
    ...:     

In [14]: class test5(metaclass=InheritFromOtherTake2):
    ...:     pass
    ...: 

In [15]: type(test5)
Out[15]: type

仅为完整性,如上所述,作为元类使用的可调用函数确实可以返回除了type(或其子类)的实例之外的其他东西。在这种情况下,从class语句体中得到的对象将不会是一个类,而是可调用函数返回的任何内容:

In [7]: def dict_maker_meta(name, bases, ns):
   ...:     return ns
   ...: 

In [8]: class test3(metaclass=dict_maker_meta):
   ...:     a = 1
   ...:     b = 2
   ...:     c = 'test'
   ...:     

In [9]: type(test3)
Out[9]: dict

In [10]: test3
Out[10]: 
{'__module__': '__main__',
 '__qualname__': 'test3',
 'a': 1,
 'b': 2,
 'c': 'test'}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接