super()究竟在Python 3中返回什么?

时间:2017-06-23 15:50:13

标签: python python-3.x inheritance

从Python3的文档super()“返回一个代理对象,该方法将方法调用委托给类型的父类或兄弟类。”这是什么意思?

假设我有以下代码:

class SuperClass():
    def __init__(self):
        print("__init__ from SuperClass.")
        print("self object id from SuperClass: " + str(id(self)))

class SubClass(SuperClass):
    def __init__(self):
        print("__init__ from SubClass.")
        print("self object id from SubClass: " + str(id(self)))
        super().__init__()


sc = SubClass()

我得到的输出是:

__init__ from SubClass.
self object id from SubClass: 140690611849200
__init__ from SuperClass.
self object id from SuperClass: 140690611849200

这意味着在行super().__init__()中,super()返回当前对象,然后将其隐式传递给超类“__init__()方法”。这是准确的还是我在这里遗漏了什么?

简而言之,我想了解以下内容:

运行super().__init__()

  1. 究竟是什么传递给__init__()以及如何?我们在super()上调用它,所以无论返回什么,都应该从我对Python的理解到目前为止传递给__init__()方法。
  2. 为什么我们不必将self传递给super().__init__()

3 个答案:

答案 0 :(得分:7)

  

返回一个代理对象,该方法将方法调用委托给父或   兄弟姐妹类型。

proxy是一个对象,充当父类的方法调用部分。这不是班级本身;相反,它只是足够的信息,因此您可以使用它来调用父类方法。

如果您致电__init__(),您将获得自己的本地子类__init__功能。当您调用super()时,您将获得该代理对象,该对象将重定向到父类方法。因此,当您调用super().__init__()时,该代理会将调用重定向到父类__init__方法。

同样,如果您要调用super().foo,您将从父类中获取foo方法 - 再次由该代理重新路由。

你清楚吗?

对OP评论的回应

  

但这必须意味着传递此代理对象   运行super()时 init ()。 init ()对吧?

错误。代理对象就像包名,例如调用math.sqrt()。你没有将数学传递给sqrt,你用它来表示你正在使用哪个sqrt。如果您想将代理传递给 init ,则调用将是 init (super())。当然,这种说法在语义上是荒谬的。

  

当我们必须实际传入self时,我的例子中是sc对象。

不,你 传入sc;这是对象创建调用的结果(内部方法__new__),其中包括init的调用。对于__init__self对象是Python运行时系统为您创建的新项目。对于大多数类方法,第一个参数(在约定中称为self,在其他语言中称为this)是调用该方法的对象。

答案 1 :(得分:4)

  

这意味着在行super().__init__()中,super()返回当前对象,然后将其隐式传递给超类“__init__()方法”。这是准确的还是我在这里遗漏了什么?

>>> help(super)  
super() -> same as super(__class__, <first argument>)

super调用返回一个代理/包装器对象,它记住了:

  • 调用super()

  • 的实例
  • 调用对象的类

  • 正在调用super()

  • 的班级

这听起来很完美。 super始终在层次结构(实际上是MRO)中获取具有您正在查找的属性的 next 类的属性。因此它不返回当前对象,而是更准确地说,它返回一个对象,该对象记住足够的信息以搜索类层次结构中更高的属性。

  
      
  1. 究竟是什么传递给__init__()以及如何?我们在super()上调用它,所以无论返回什么,都应该从我对Python的理解到目前为止传递给__init__()方法。
  2.   

你几乎是对的。但super喜欢在我们身上耍花招。 super类定义__getattribute__,此方法负责属性搜索。当您执行以下操作时:super().y()super.__getattribute__会被称为搜索y。找到y后,它会将调用super调用的实例传递给y。此外,super具有__get__方法,这使得它成为描述符,我将在此省略描述符的详细信息,请参阅文档以了解更多信息。这也回答了你的第二个问题,即为什么self没有明确传递。

*注意:super有点不同,依​​赖于一些魔力。几乎所有其他类,行为都是一样的。那就是:

a = A()  # A is a class 
a.y()    # same as A.y(a), self is a 

但是super是不同的:

class A:
    def y(self):
        return self
class B(A):
    def y(self)
        return super().y()   # equivalent to: A.y(self)
b = B()
b.y() is b  # True: returns b not super(), self is b not super()

答案 2 :(得分:1)

我写了一个简单的测试来调查CPython对super的作用:

class A:
    pass

class B(A):
    def f(self):
        return super()

    @classmethod
    def g(cls):
        return super()

    def h(selfish):
        selfish = B()
        return super()

class C(B):
    pass

c = C()
for method in 'fgh':
    super_object = getattr(c, method)()
    print(super_object, super_object.__self__, super_object.__self_class__, super_object.__thisclass__)  # (These methods were found using dir.)

零参数super调用返回一个存储三件事的对象:

  • __self__存储名称与方法的第一个参数匹配的对象 - 即使该名称已被重新分配。
  • __self_class__存储其类型,或者在类方法的情况下存储。
  • __thisclass__存储定义方法的类。

(遗憾的是__thisclass__是以这种方式实现的,而不是在方法上获取属性,因为它使得无法将super的零参数形式用于元编程。)< / p>

super返回的对象实现getattribute,它将方法调用转发到__mro__之后__self_class__ __thisclass__ key2中找到的类型。< / p>