为什么一个继承自“object”的Python类在其__init__方法中要调用“super”?

7
我在查看requests模块的源代码时发现了这段代码:
class Response(object):
    """The :class:`Response <Response>` object, which contains a
    server's response to an HTTP request.
    """

    def __init__(self):
        super(Response, self).__init__()
        ... more init method...

我对super()的理解是,这个调用根本不会做任何事情。我发现有关相当多 问题 关于超类调用, 但所有的都是从其他类的子类中工作,而不是object本身。Python文档也没有提到这种构造方式。

我突然想到这可能只是一个错误,如果你用git blame命令查看引入该行代码的提交记录,你会发现在编写代码时,ResponseBaseResponse的子类。这行代码是从类重构中保留下来的,还是说这个super()调用实际上有作用呢?


据我所知,在这里是不必要的,但也无害的。对于任何子类来说,这都是一个标准操作,因为调用超类构造函数并不是自动的 - 这甚至可能是由 RAD 工具自动插入的。 - Corley Brigman
3个回答

4

如果Response成为多重继承树的一部分,那么这段代码可能真正做些什么。在"MRO"列表中的下一个类可以是除了object之外的其他东西!在这种情况下,对super的调用可能非常必要

例如:

class NewerClass(A, Response):
    def __init__(self):
        ... do stuff ...
        super(NewerClass, self).__init__()

从这个类定义中,如果不知道 A 的层次结构,你就无法确定 super(Response, self).__init__() 调用的下一个类是什么。它可能是 object,也可能是 A 的基类之一。
我认为问题之一在于名称 super 会引起混淆。它不像 Smalltalk 的 super 变量那样,除非你只使用单继承,它也不返回“超类”对象。相反,它正在根据 MRO(方法解析顺序)确定要使用的“下一个”类。
有关此主题的经典文章包括: Python's Super Considered SuperPython's Super Considered Harmful

有趣的观点。避免多重继承的另一个原因,除非你真的真的...真的知道你在做什么并需要它。 - Davidmh
很多时候多重继承只用来添加“mixin”或“interface”类。这种类型的多类继承更容易处理。 - darinbob

2
Corley Brigman's comment所提到的,这是不必要但无害的。
背景介绍一下,在 Kenneth 对 Requests 1.0 进行开发时,添加了 BaseResponse 类。1.0 版本的代码更改引入了 传输适配器,使得可以为某些 HTTP 端点(或者 非 HTTP 端点)定义具体的行为。传输适配器接口的一个重要部分是 HTTPAdapter.build_response() 方法,该方法接收从 HTTPAdapter.send() 返回的原始响应,并从中构建出一个 Requests 的 Response 对象。
很明显,肯尼思认为具有某种形式的抽象基类对于Response来说具有潜在的实用性,这将允许传输适配器返回具有非常不同行为的Response对象,而不仅仅是标准的HTTP Response对象。因此,在子类中重构大部分逻辑的ABC似乎是有道理的。
在重构的后期,这个ABC又被移除了,因为它增加了不必要的复杂性。事实上,想要定义特定的Response对象的人可以简单地继承Response,而不需要一个没有太多用处的ABC。这使得主流用例(原始请求)在代码中更加清晰,并且几乎没有任何效用损失。
当BaseRequest类被移出时,这行代码被忽略了,但由于它没有引起问题,因此从未需要删除它。

1
对于阅读此答案的任何人,“ABC”=“抽象基类”。我在第一次阅读时就被这个搞糊涂了。 - Eric Dand

0

darinbob 是正确的。 当谈到单一继承时,这是不必要的,但是在多重继承中,情况就不同了:

class WithSuper:
    def __init__(self):
        print('With super')
        super().__init__()

class NoSuper:
    def __init__(self):
        print('No super')

class TestOne(WithSuper, NoSuper):
    def __init__(self):
        print('Test 1')
        super().__init__()

class TestTwo(NoSuper, WithSuper):
    def __init__(self):
        print('Test 2')
        super().__init__()


one = TestOne()
print(TestOne.__mro__)

print('\n' * 2)

two = TestTwo()
print(TestTwo.__mro__)

使用Python3运行上述代码,您将看到差异。

Test 1
With super
No super
(<class '__main__.TestOne'>, <class '__main__.WithSuper'>, <class '__main__.NoSuper'>, <class 'object'>)



Test 2
No super
(<class '__main__.TestTwo'>, <class '__main__.NoSuper'>, <class '__main__.WithSuper'>, <class 'object'>)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接