元类的继承

Lif*_*ang 11 python inheritance metaclass

在这个众所周知的答案中解释了Python中的元类.它提到该__metaclass__属性不会被继承.

但事实上,我在Python中尝试过:

class Meta1(type):
    def __new__(cls, clsname, bases, dct):
        print "Using Meta1"
        return type.__new__(cls, clsname, bases, dct)

# "Using Meta1" printed
class Foo1:
    __metaclass__ = Meta1

# "Using Meta1" printed
class Bar1(Foo1):
    pass
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,无论是FooBar使用Meta1作为元类和打印字符串预期.

但是在下面的示例中,type(...)返回的是when 而不是type.__new__(...),继承了元类:

class Meta2(type):
    def __new__(cls, clsname, bases, dct):
        print "Using Meta2"
        return type(clsname, bases, dct)

# "Using Meta2" printed
class Foo2:
    __metaclass__ = Meta2

# Nothing printed
class Bar2(Foo2):
    pass
Run Code Online (Sandbox Code Playgroud)

检查__metaclass____class__属性,我可以看到:

print Foo1.__metaclass__ # <class '__main__.Meta1'>
print Bar1.__metaclass__ # <class '__main__.Meta1'>
print Foo2.__metaclass__ # <class '__main__.Meta2'>
print Bar2.__metaclass__ # <class '__main__.Meta2'>

print Foo1.__class__ # <class '__main__.Meta1'>
print Bar1.__class__ # <class '__main__.Meta1'>
print Foo2.__class__ # <type 'type'>
print Bar2.__class__ # <type 'type'>
Run Code Online (Sandbox Code Playgroud)

结论:

  1. 双方__metaclass____class__ 从基类继承.

  2. Meta2将使用定义的创建行为Foo2,尽管Foo2.__class__实际上是type.

  3. __metaclass__在属性Bar2Meta2,但创造的行为Bar2不会受到影响.换句话说,Bar2使用type它作为"真正的"元类而不是Meta2.

这些观察使得我的继承机制变得__metaclass__模糊.

我的猜测是:

  1. 当直接将类(例如Meta1)分配给__metaclass__另一个类'Foo1'的__metaclass__属性时,它就是生效的属性.

  2. 子类__metaclass__在定义时未明确设置属性.该__class__属性,而不是__metaclass__基类的属性将决定子类的"真正的"元类.

我猜是正确的吗?Python如何处理元类的继承?

jsb*_*eno 7

您正在猜测很多,而Python的极简主义和“特殊情况还不足以打破规则。” 指令,比这更容易理解。

在Python2中,__metaclass__在类创建时使用类主体中的属性来调用该类将成为的“类”。通常,它是名为的类type。为了明确起见,那一刻是在解析器解析了类主体之后,在编译器将其编译为代码对象之后,以及在程序运行时实际运行之后,并且仅__metaclass__在该类主体中明确提供了该时刻。

因此,让我们检查一下这种情况下的情况:

class A(object):
    __metaclass__ = MetaA

class B(A):
    pass
Run Code Online (Sandbox Code Playgroud)

A已经__metaclass__在它的身上- MetaA被称为代替type,使之成为“类对象”。 B没有__metaclass__在它的身上。创建__metaclass__属性后,如果您只是尝试访问该 属性,则该属性与其他属性一样将是可见的,因为Python将从超类获取它A。如果你检查A.__dict__,你会看到 __metaclass__,如果你查B.__dict__不知道。

创建B时根本不使用A.__metaclass__属性。如果在声明之前进行更改,则仍将使用与-相同的元类,因为Python确实在不使用显式声明的情况下将父类的类型用作元类。ABA__metaclass__

为了显示:

In [1]: class M(type): pass

In [2]: class A(object): __metaclass__ = M

In [3]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(A.__class__, A.__metaclass__, A.__dict__.get("__metaclass__"), type(A))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: <class '__main__.M'>, type: <class '__main__.M'>

In [4]: class B(A): pass

In [5]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(B.__class__, B.__metaclass__, B.__dict__.get("__metaclass__"), type(B))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: None, type: <class '__main__.M'>

In [6]: A.__metaclass__ = type

In [8]: class C(A): pass

In [9]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(C.__class__, C.__metaclass__, C.__dict__.get("__metaclass__"), type(C))
class: <class '__main__.M'>, metaclass_attr: <type 'type'>, metaclass_in_dict: None, type: <class '__main__.M'>
Run Code Online (Sandbox Code Playgroud)

此外,如果您尝试通过调用来创建一个类,type而不是使用带有class声明的主体,那么这__metaclass__也只是一个普通属性:

In [11]: D = type("D", (object,), {"__metaclass__": M})

In [12]: type(D)
type
Run Code Online (Sandbox Code Playgroud)

到目前为止,总结__metaclass__Python 2中的属性只有在将其显式放置在类主体声明中(作为classblock语句执行的一部分)时才是特殊的。这是普通属性,之后没有特殊属性。

Python3都摆脱了这种奇怪的“ __metaclass__属性现在不好”,并允许通过更改语法以指定元类来进一步自定义类主体。(就像metaclassclass声明本身上声明它是一个“ 命名参数”一样)

现在,引起您怀疑的第二部分是:如果在__new__元类的方法中调用type而不是 type.__new__,则无法type从派生的元类中调用Python 。当您调用时type.__new__,您会将运行时传递的cls元类__new__本身的属性作为第一个参数传递:这就是将结果类标记为的子类实例的原因type就像继承适用于Python中的任何其他类一样,因此这里的“没有特殊行为”:

因此,找出差异:

class M1(type):
    def __new__(metacls, name, bases, attrs):
         cls = type.__new__(metacls, name, bases, attrs)
         # cls now is an instance of "M1"
         ...
         return cls


class M2(type):
    def __new__(metacls, name, bases, attrs):
         cls = type(cls, name, bases, attrs)
         # Type does not "know" it was called from within "M2"
         # cls is an ordinary instance of "type"
         ...
         return cls
Run Code Online (Sandbox Code Playgroud)

可以在交互式提示中看到:

In [13]: class M2(type):
   ....:     def __new__(metacls, name, bases, attrs):
   ....:         return type(name, bases, attrs)
   ....:     

In [14]: class A(M2): pass

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

In [16]: class A(M2): __metaclass__ = M2

In [17]: A.__class__, A.__metaclass__
Out[17]: (type, __main__.M2)
Run Code Online (Sandbox Code Playgroud)

(请注意,metaclass __new__方法的第一个参数是metaclass本身,因此,metaclscls在您的代码中和在许多“野蛮的”代码中更正确地命名)