代码之家  ›  专栏  ›  技术社区  ›  PapaDiHatti

为什么元类应该从类型继承?

  •  1
  • PapaDiHatti  · 技术社区  · 6 年前

    我们可以将函数用作元类,我的理解是它们不是派生自如下所示的类型:

    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)
    

    但是,当我们编写元类时,它应该继承自类型,我无法理解这背后的原因:

    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公司 类应该是 继承 类型 新的 初始化 , 呼叫 . 还有错误“descriptor” 初始化

    1 回复  |  直到 6 年前
        1
  •  3
  •   jsbueno    6 年前

    在普通的Python代码中,实际创建具有二进制结构和所有所需字段的内存类对象的唯一调用是 type.__new__ . 使用元类这样的可调用函数只需实例化 type

    可以使用本机C代码,甚至在纯Python中创建另一个“基本元类”,调用本机OS内存分配函数并填充相同的内容 structure defined for a "type object" ,但是,这只能完成完全相同的任务 已经这样做了,所以这将是一个复杂的、容易出错的、无法进一步改进的轮子的重新发明,因为产生的二进制布局必须与该结构中定义的相同(即,实现“神奇功能”的方法的指针,如 __init__ __geitem__ 以此类推,必须与结构中的偏移量相同)

    (此结构中数据的附加字段,甚至精致值都可以通过代码继承类型来完成—因此无论如何都可以通过普通元类来完成)

    类型。\u新__ (函数通过调用 类型 __class__ 属性)是有效的元类。如果元类以 __metaclass__ 类型 ,也就是元类本身。否则,如果它是一个普通函数,只是作为一个类工厂调用 类型 在它的主体中,有效元类只是类型。

    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
    

    那么,为什么一个类不能从 类型 被使用?

    尝试这样做时出现的错误是由于调用 类型。\u新__

    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
    

    类型。\u新__ 使用类型为的有效子类作为第一个参数:

    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
    

    正如上面提到的,为了完整性,作为元类使用的callable确实有可能返回类型(或其子类)的实例以外的内容。在这种情况下,由 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'}