调用super()时,元类如何与MRO列表一起使用? [英] how does metaclass work with the MRO list when super() is called?

查看:81
本文介绍了调用super()时,元类如何与MRO列表一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码示例让我很困惑:

class Meta_1(type):
    def __call__(cls, *a, **kw):             # line 1
        print("entering Meta_1.__call__()")  

        print(cls)                           # line 4
        print(cls.mro())                     # line 5
        print(super(Meta_1, cls).__self__)   # line 6

        rv = super(Meta_1, cls).__call__(*a, **kw)  # line 7
        print("exiting Meta_1.__call__()")
        return rv


class Car(object, metaclass=Meta_1):

    def __new__(cls, *a, **kw):
        print("Car.__new__()")
        rv = super(Car, cls).__new__(cls, *a, **kw)
        return rv

    def __init__(self, *a, **kw):
        print("Car.__init__()")
        super(Car,self).__init__(*a, **kw)

if __name__ == '__main__':

    c = Car()

此代码的打印消息为:

entering Meta_1.__call__()
<class '__main__.Car'>                      # line 4
[<class '__main__.Car'>, <class 'object'>]  # line 5
<class '__main__.Car'>                      # line 6
Car.__new__()
Car.__init__()
exiting Meta_1.__call__()

结果显示第4行的clsCar类,并且其MRO列表是:
[<class '__main__.Car'>, <class 'object'>]

但是,第6行显示super(Meta_1, cls).__self__也是Car类.

我真的很困惑:

  1. 在第7行中,看来super(Meta_1, cls).__call__(*a, **kw)最终导致了type.__call__. 但是,据我所知,super(arg1, arg2)将调查第二个输入参数的MRO,以找到第一个输入参数,并将下一个类返回给它.但是在我的代码的第6和第7行中,第二个参数(Car)的MRO不包含第一个输入参数(Meta_1),您无法在Car的MRO中找到Meta_1.那么为什么super(Meta_1, cos)带我们去调用type.__call__呢?

2.如果super(Meta_1, cls).__self__Car类,那么第7行表示正在调用的是Car__call__吗?但是调用Car类使我们首先进入了第1行,对吗?那不是循环吗?

解决方案

重要的是要注意将什么值用作super的每个参数. super的主要目的是根据某些方法解析顺序(MRO)执行属性查找.第二个参数确定要使用哪个MRO;首先确定开始的位置.

MRO始终由 class 定义;在对实例执行方法解析时,我们使用该实例为类型的类的MRO.

在班上

class Meta_1(type):
    def __call__(cls, *a, **kw):             # line 1
        print("entering Meta_1.__call__()")  

        print(cls)                           # line 4
        print(cls.mro())                     # line 5
        print(super(Meta_1, cls).__self__)   # line 6

        rv = super(Meta_1, cls).__call__(*a, **kw)  # line 7
        print("exiting Meta_1.__call__()")
        return rv

我们看到了super的两种用法.两者都采用相同的论点. cls是作为第一个参数传递给Meta_1.__call__的某个对象.这意味着我们将使用type(cls)提供的MRO,并且将使用在之后 Meta_1之后找到的第一个类,该类提供了所需的方法. (在第一次调用中,__self__是代理对象本身的属性,而不是其代理super返回的类的属性或方法.)

运行代码时,您会看到cls已绑定到Car类型的对象.这是因为Car()是由type(Car).__call__()实现的;由于Car使用Meta_1作为其元类,所以type(Car)Meta_1.

cls.mro()是无关紧要的,因为那是cls instances 所使用的MRO.

Meta_1本身的MRO可以通过

查看

>>> Meta_1.mro(Meta_1)
[<class '__main__.Meta_1'>, <class 'type'>, <class 'object'>]

(mrotype类的实例方法,因此需要type的表面冗余实例作为参数.请记住,cls.mro()等效于type(cls).mro(cls).)

所以第7行是对type.__call__的调用,以便创建cls可以返回的cls实例.

I'm really confused by the following code sample:

class Meta_1(type):
    def __call__(cls, *a, **kw):             # line 1
        print("entering Meta_1.__call__()")  

        print(cls)                           # line 4
        print(cls.mro())                     # line 5
        print(super(Meta_1, cls).__self__)   # line 6

        rv = super(Meta_1, cls).__call__(*a, **kw)  # line 7
        print("exiting Meta_1.__call__()")
        return rv


class Car(object, metaclass=Meta_1):

    def __new__(cls, *a, **kw):
        print("Car.__new__()")
        rv = super(Car, cls).__new__(cls, *a, **kw)
        return rv

    def __init__(self, *a, **kw):
        print("Car.__init__()")
        super(Car,self).__init__(*a, **kw)

if __name__ == '__main__':

    c = Car()

The print message for this code is:

entering Meta_1.__call__()
<class '__main__.Car'>                      # line 4
[<class '__main__.Car'>, <class 'object'>]  # line 5
<class '__main__.Car'>                      # line 6
Car.__new__()
Car.__init__()
exiting Meta_1.__call__()

The result shows that cls of line 4 is the Car class and its MRO list is:
[<class '__main__.Car'>, <class 'object'>]

However, line 6 shows that super(Meta_1, cls).__self__ is also the Car class.

I am really confused that:

  1. In line 7, It seems that super(Meta_1, cls).__call__(*a, **kw) eventually lead to type.__call__. But, to my knowledge, super(arg1, arg2) will look into the MRO of the second input argument to find the first input argument, and return the next class to it. But in line 6 and 7 of my code, the MRO for 2nd argument(Car), does not contain the 1st input argument(Meta_1), you cannot find Meta_1 in the MRO for Car. so why would super(Meta_1, cos) take us to invoke type.__call__ ??

2. if super(Meta_1, cls).__self__ is the Car class, then line 7 means it's Car's __call__ that's being called? But calling the Car class took us to line 1 in the first place, right? wouldn't that be a loop?

解决方案

It's important to pay attention to what values are being used as each argument to super. The primary purpose of super is to perform attribute lookup according to some method-resolution order (MRO). The second argument determines which MRO to use; the first determines where to start looking.

An MRO is always defined by a class; when performing method resolution on an instance, we use the MRO of the class of which that instance is a type.

In the class

class Meta_1(type):
    def __call__(cls, *a, **kw):             # line 1
        print("entering Meta_1.__call__()")  

        print(cls)                           # line 4
        print(cls.mro())                     # line 5
        print(super(Meta_1, cls).__self__)   # line 6

        rv = super(Meta_1, cls).__call__(*a, **kw)  # line 7
        print("exiting Meta_1.__call__()")
        return rv

we see two uses of super. Both take the same arguments. cls is some object passed as the first argument to Meta_1.__call__. That means we'll use the MRO provided by type(cls), and we'll use the first class found after Meta_1 that provides the desired method. (In the first call, __self__ is an attribute of the proxy object itself, rather than an attribute or method of the class whose proxy super returns.)

When you run your code, you see that cls is bound to your Car type object. That's because Car() is implemented by type(Car).__call__(); since Car uses Meta_1 as its metaclass, type(Car) is Meta_1.

cls.mro() is irrelevant, because that's the MRO used by instances of cls.

The MRO of Meta_1 itself can be seen with

>>> Meta_1.mro(Meta_1)
[<class '__main__.Meta_1'>, <class 'type'>, <class 'object'>]

(mro is an instance method of the type class, and so requires the seemingly redundant instance of type as an argument. Keep in mind that cls.mro() is equivalent to type(cls).mro(cls).)

So line 7 is a call to type.__call__, in order to create an instance of cls that Meta_1.__call__ can return.

这篇关于调用super()时,元类如何与MRO列表一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆