当调用super()时,元类如何与MRO列表一起工作?
当调用super()时,元类如何与MRO列表一起工作?
以下是对上述代码示例的翻译:\n我对以下代码示例感到非常困惑:\n
class Meta_1(type): def __call__(cls, *a, **kw): # 第1行 print("进入 Meta_1.__call__()") print(cls) # 第4行 print(cls.mro()) # 第5行 print(super(Meta_1, cls).__self__) # 第6行 rv = super(Meta_1, cls).__call__(*a, **kw) # 第7行 print("退出 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()
\n此代码的打印信息为:\n
进入 Meta_1.__call__()# 第4行 [ , ] # 第5行 # 第6行 Car.__new__() Car.__init__() 退出 Meta_1.__call__()
\n结果显示,第4行的\n然而,第6行显示cls
是Car
类,并且它的MRO列表为:
\n[
super(Meta_1, cls).__self__
也是Car
类。\n我真的很困惑:\n
- \n
- 在第7行中,似乎
super(Meta_1, cls).__call__(*a, **kw)
最终导致调用了type.__call__
。\n但是,据我所知,super(arg1, arg2)
会查找第二个输入参数的MRO以找到第一个输入参数,并返回它的下一个类。\n但在我的代码的第6行和第7行中,第2个参数(Car
)的MRO中并不包含第1个输入参数(Meta_1
),你无法在Car
的MRO中找到Meta_1
。那么为什么super(Meta_1, cos)
会让我们调用type.__call__
?
\n
\n2. 如果super(Meta_1, cls).__self__
是Car
类,那么第7行意味着正在调用Car
的__call__
?但是调用Car
类会导致我们进入第1行,对吗?这不会造成循环吗?
元类是Python中一种特殊的类,用于创建其他类。它的主要作用是控制类的创建和初始化过程。当使用超类方法`super()`时,元类会与MRO列表(Method Resolution Order,方法解析顺序)一起工作。
在上述引用中,原作者解释了`super()`如何使用元类和MRO列表。`super()`会使用当前类(`cls`)来查找MRO列表,但并不是直接使用`cls.__mro__`来查找元类。相反,它会查找`cls`的类(也就是元类),然后从该类中查找MRO列表。
这意味着,当我们在一个类中调用`super()`时,它会首先查找该类的元类,然后再查找元类的MRO列表。这种行为与直接调用`cls.__mro__`不同,因为`cls`的MRO列表是与`cls`所继承的类相关的,而不是与元类相关的。
解决这个问题的方法是使用`super()`来调用父类的方法,而不是直接使用`cls.__mro__`。这样,`super()`会自动查找正确的MRO列表,以确保方法顺序的正确性。
总结起来,当我们在使用元类时,并在类中使用`super()`调用父类的方法时,我们需要意识到`super()`实际上是通过查找元类和MRO列表来确定方法的调用顺序。我们应该避免直接使用`cls.__mro__`,而是使用`super()`来确保正确的方法解析顺序。
在这段内容中,提到了在使用super()函数时,Metaclass是如何与MRO列表相结合的。根据内容,问题的出现原因是因为在使用super()函数时,需要注意每个参数所使用的值。super()函数的主要目的是根据某个方法解析顺序(MRO)进行属性查找。第二个参数确定要使用哪个MRO,而第一个参数确定从哪里开始查找。
在这段内容中,给出了一个名为Meta_1的类的示例。在这个类中,有两个使用super()函数的地方,它们都使用相同的参数。cls是作为第一个参数传递给Meta_1.__call__方法的某个对象。这意味着我们将使用type(cls)提供的MRO,并且我们将使用在Meta_1之后找到的第一个提供所需方法的类。
当运行代码时,可以看到cls绑定到了Car类型的对象。这是因为Car()是由type(Car).__call__()实现的;由于Car使用Meta_1作为其元类,所以type(Car)是Meta_1。
根据给出的内容,可以得出解决方法:在使用super()函数时,需要注意每个参数所使用的值。第一个参数决定从哪里开始查找,第二个参数决定要使用的MRO。如果想使用type(cls)的MRO,可以将type(cls)作为第二个参数传递给super()函数。
,当调用super()函数时,Metaclass是根据MRO列表来工作的。通过指定参数,可以确定从哪里开始查找以及使用哪个MRO。这样可以确保在使用super()函数时,可以正确地进行属性查找和方法解析。
元类是Python中非常强大的概念,它允许我们在定义类时自定义类的行为和属性。然而,在使用元类时,某些概念可能会令人困惑,特别是与类的继承层次结构和super()函数的关系。
首先,需要明确元类与类的继承层次结构是不同的概念。元类是类的类型,它具有创建类对象本身的模板和方法。元类有构建类的继承顺序(MRO)的机制,并调用类的__new__和__init__方法。当调用一个类对象时,实际上是调用了元类的__call__方法来创建一个新的实例。
其次,super()函数并不是实际的超类,它是一个代理对象,将任何属性检索或方法调用转发到适当的超类。super()函数的内部机制是将调用super()的实例作为自己的__self__属性。当super对象被用作代理来访问实例的"超类"的属性或方法时,它会使用__self__属性指向的实例。
当在元类中使用super()时,被代理的类是元类的超类,而不是Car的超类。这就解释了为什么super(Meta_1, cls).__self__是Car类,并且为什么在元类的__call__方法中调用Car类的__call__方法不会导致循环调用。
在super函数的第一个参数中传递Meta_1是因为通常情况下,第一个参数是定义方法所在的类。它作为MRO搜索的起始点;super(A, obj)返回一个代理对象,该对象在obj的MRO中紧跟在A之后的类。在元类中使用super()时,它并不按照通常的方式使用第一个和第二个参数来代理obj的MRO中A之后的类,而是代理元类的超类。
总之,元类和类的继承层次结构是不同的概念。元类负责创建类对象本身,并通过其__call__方法来创建实例。super()函数是一个代理对象,用于访问实例的"超类"的属性和方法。在元类中使用super()时,被代理的类是元类的超类,而不是Car的超类。
请注意,尽管元类和super()函数非常强大,但在大多数情况下,我们并不需要在代码中使用它们。只有在特定情况下,才需要在子类中使用super()函数。