习惯从类型继承元类?

Nik*_*win 29 python metaclass

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

def metacls(clsName, bases, atts):
    ....
    return type(clsName, bases, atts)
Run Code Online (Sandbox Code Playgroud)

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

class Metacls(type):
    def __new__(meta, clsName, bases, atts):
        ....
        return type.__new__(meta, clsName, bases, atts)
Run Code Online (Sandbox Code Playgroud)

据我所知,这些都会做同样的事情.是否有任何理由使用基类?这是习惯吗?

esw*_*ald 39

有微妙的差异,主要与继承有关.当使用函数作为元类时,结果类实际上是一个实例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'>
Run Code Online (Sandbox Code Playgroud)

请注意,在定义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'>
Run Code Online (Sandbox Code Playgroud)

注意差异:创建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
Run Code Online (Sandbox Code Playgroud)

这是使用元类进行多重继承的主要限制.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'>
Run Code Online (Sandbox Code Playgroud)

这就是我super()在元类__new__函数中使用的原因:因此每个函数都可以调用MRO中的下一个函数.

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