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

习惯于从类型继承元类?

  •  29
  • Nikwin  · 技术社区  · 16 年前

    我一直在尝试理解python元类,所以一直在研究一些示例代码。据我所知,python元类可以是任何可调用的。所以,我可以像

    def metacls(clsName, bases, atts):
        ....
        return type(clsName, bases, atts)
    

    但是,我看到很多人用以下方式编写元类:

    class Metacls(type):
        def __new__(meta, clsName, bases, atts):
            ....
            return type.__new__(meta, clsName, bases, atts)
    

    据我所知,这两个都会做同样的事情。有没有理由使用基类来代替呢?这是惯例吗?

    1 回复  |  直到 11 年前
        1
  •  39
  •   eswald    16 年前

    有细微的差异,主要与继承有关。当使用 作为元类,结果类实际上是 type , 并且可以无限制地从继承;但是,元类函数 永远不会为此类子类调用。当使用的子类 类型 作为一个 元类,生成的类将是该元类的实例,并且 它的任何子类;但是,多重继承将受到限制。

    说明差异:

    >>> def m1(name, bases, atts):
    >>>     print "m1 called for " + name
    >>>     return type(name, bases, atts)
    >>>
    
    >>> def m2(name, bases, atts):
    >>>     print "m2 called for " + name
    >>>     return type(name, bases, atts)
    >>>
    
    >>> class c1(object):
    >>>     __metaclass__ = m1
    m1 called for c1
    
    >>> type(c1)
    <type 'type'>
    
    >>> class sub1(c1):
    >>>     pass
    
    >>> type(sub1)
    <type 'type'>
    
    >>> class c2(object):
    >>>     __metaclass__ = m2
    m2 called for c2
    
    >>> class sub2(c1, c2):
    >>>     pass
    
    >>> type(sub2)
    <type 'type'>
    

    注意,在定义Sub1和Sub2时,没有调用元类函数。 它们将被创建,就像c1和c2没有元类一样,而是 是在创造之后被操纵的。

    >>> class M1(type):
    >>>     def __new__(meta, name, bases, atts):
    >>>         print "M1 called for " + name
    >>>         return super(M1, meta).__new__(meta, name, bases, atts)
    
    >>> class C1(object):
    >>>     __metaclass__ = M1
    M1 called for C1
    
    >>> type(C1)
    <class '__main__.M1'>
    
    >>> class Sub1(C1):
    >>>     pass
    M1 called for Sub1
    
    >>> type(Sub1)
    <class '__main__.M1'>
    

    注意已经存在的差异:创建Sub1时调用了M1,并且两者都调用了 类是M1的实例。我在用 super() 对于这里的实际创造, 原因将在以后明确。

    >>> class M2(type):
    >>>     def __new__(meta, name, bases, atts):
    >>>         print "M2 called for " + name
    >>>         return super(M2, meta).__new__(meta, name, bases, atts)
    
    >>> class C2(object):
    >>>     __metaclass__ = M2
    M2 called for C2
    
    >>> type(C2)
    <class '__main__.M2'>
    
    >>> class Sub2(C1, C2):
    >>>     pass
    M1 called for Sub2
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 23, in __new__
    TypeError: Error when calling the metaclass bases
        metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
    

    这是对具有元类的多个继承的主要限制。 python不知道m1和m2是否兼容元类, 所以它迫使你创建一个新的,以保证它做你需要的。

    >>> class M3(M1, M2):
    >>>     def __new__(meta, name, bases, atts):
    >>>         print "M3 called for " + name
    >>>         return super(M3, meta).__new__(meta, name, bases, atts)
    
    >>> class C3(C1, C2):
    >>>     __metaclass__ = M3
    M3 called for C3
    M1 called for C3
    M2 called for C3
    
    >>> type(C3)
    <class '__main__.M3'>
    

    这就是我为什么用 超级() 在元类中 __new__ 功能:所以每一个 可以打给MRO的下一个。

    某些用例可能需要类的类型 类型 或者可能想要 为了避免继承问题,在这种情况下,元类函数可能是 前进的道路。在其他情况下,班级的类型可能真的很重要, 或者您可能希望对所有子类进行操作,在这种情况下,子类化 类型 会是个更好的主意。请随意使用最适合的款式 任何特定情况。