【问题标题】:Customary To Inherit Metaclasses From type?习惯从类型继承元类?
【发布时间】:2011-01-10 03:03:42
【问题描述】:

我一直在尝试理解 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)

据我所知,它们都会做同样的事情。有什么理由改用基类吗?习惯吗?

【问题讨论】:

    标签: python metaclass


    【解决方案1】:

    存在细微差别,主要与继承有关。当使用一个 函数作为元类,生成的类实际上是type的实例, 并且可以不受限制地继承;然而,元类函数 永远不会为此类子类调用。当使用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__ 函数中使用super() 的原因:所以每一个 可以调用 MRO 中的下一个。

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

    【讨论】:

    • 感谢您花时间解释。
    • 非常感谢,难得看到这么清晰的回答。
    猜你喜欢
    • 2012-03-11
    • 2014-04-03
    • 2018-06-23
    • 1970-01-01
    • 2019-08-18
    • 2019-02-18
    • 2010-09-27
    • 1970-01-01
    • 2019-07-11
    相关资源
    最近更新 更多