在 Python 个请求中使用 super() 继承多个 Session

Using super() to inherit from multiple Sessions in Python requests

为了以 DRY 方式编写许多 HTTP 请求,我想定义 requests Session 的几个子类,并根据需要从它们继承。我试过以下方法:

import requests, time

requestbin_URL = 'http://requestb.in/1nsaz9y1'      # For testing only; remains usable for 48 hours
auth_token = 'asdlfjkwoieur182932385'               # Fake authorization token

class AuthorizedSession(requests.Session):
    def __init__(self, auth_token):
        super().__init__()
        self.auth_token = auth_token
        self.headers.update({'Authorization': 'token=' + self.auth_token})

class JSONSession(requests.Session):
    def __init__(self):
        super().__init__()
        self.headers.update({'content-type': 'application/json'})

class DebugSession(requests.Session):
    def __init__(self, verify=False):
        super().__init__()
        self.verify = verify

class AuthorizedJSONDebugSession(AuthorizedSession, JSONSession, DebugSession):
    def __init__(self, auth_token, verify=False):
        super().__init__(auth_token, verify=verify)

with AuthorizedJSONDebugSession() as s:
    response = s.post(requestbin_URL, data={"key" : "value"})

但是,如果我尝试 运行 这个(在 Python 3 中),我得到

In [9]: exec(open('requestbin_test_python3.py').read())
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-cc670860ddd1> in <module>()
----> 1 exec(open('requestbin_test_python3.py').read())

<string> in <module>()

TypeError: __init__() missing 1 required positional argument: 'auth_token'

我希望 super().__init__ 应用于 AuthorizedJSONDebugSession 将是 'clever enough' 以了解 auth_token 用于初始化 AuthorizedSessionverify 初始化 DebugSession。 (仅使用 AuthorizedSessionJSONSession 的类似示例确实有效)。

然而,情况似乎并非如此。我如何修改此代码以使其工作?

我非常怀疑这种方法在一般情况下是否有效,但您可以尝试显式调用所需的 __init__ 方法:

import requests, time

requestbin_URL = 'http://requestb.in/1nsaz9y1'      # For testing only; remains usable for 48 hours
auth_token = 'asdlfjkwoieur182932385'               # Fake authorization token

class AuthorizedSession(requests.Session):
    def __init__(self, auth_token):
        super().__init__()
        self.auth_token = auth_token
        self.headers.update({'Authorization': 'token=' + self.auth_token})

class JSONSession(requests.Session):
    def __init__(self):
        super().__init__()
        self.headers.update({'content-type': 'application/json'})

class DebugSession(requests.Session):
    def __init__(self, verify=False):
        super().__init__()
        self.verify = verify

class AuthorizedJSONDebugSession(AuthorizedSession, JSONSession, DebugSession):
    def __init__(self, auth_token, verify=False):
        AuthorizedSession.__init__(self, auth_token)
        JSONSessions.__init__(self)
        DebugSession.__init__(self, verify=verify)

with AuthorizedJSONDebugSession(auth_token) as s:
    response = s.post(requestbin_URL, data={"key" : "value"})

看看这个

class AuthorizedJSONDebugSession(AuthorizedSession, JSONSession, DebugSession):
    def __init__(self, auth_token, verify=False):
        super().__init__(auth_token, verify=verify)

with AuthorizedJSONDebugSession() as s:
    response = s.post(requestbin_URL, data={"key" : "value"})

您的问题是 with AuthorizedJSONDebugSession() 您没有提供任何参数,但 init 方法需要 1 个身份验证令牌参数。 def __init__(self, auth_token, verify=False)

这与尝试执行以下操作基本相同:

>>> x=10
>>> def foo(x):
...     return x + 1
...
>>> foo()
TypeError: foo() missing 1 required positional argument: 'x'

在函数范围内,x与全局x

不一样

要解决此问题,请执行以下操作:

with AuthorizedJSONDebugSession(auth_token) as s:
    response = s.post(requestbin_URL, data={"key" : "value"})

或者,您可以更改 init 方法来执行此操作,但我不推荐这样做。

def __init__(self, auth_token=auth_token, verify=False)

您还应该注意如何使用 super() 传递参数。在这方面,您的代码还有其他一些问题。您必须确保 MRO 中的每个 class 都在与您想要做的事情合作,并将参数传递给 superclass.

你也应该熟悉Python的super是如何工作的,这与其他语言不同。

处理这个问题的标准方法是只允许关键字参数并使所有 类 接受 **kwargs:

import requests, time

requestbin_URL = 'http://requestb.in/1nsaz9y1'      # For testing only; remains usable for 48 hours
auth_token = 'asdlfjkwoieur182932385'               # Fake authorization token

class AuthorizedSession(requests.Session):
    def __init__(self, **kwargs):
        try:
            self.auth_token = kwargs.pop('auth_token')
        except KeyError:
            raise TypeError('Missing auth_token parameter!')

        super().__init__(**kwargs)
        self.headers.update({'Authorization': 'token=' + self.auth_token})

class JSONSession(requests.Session):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.headers.update({'content-type': 'application/json'})

class DebugSession(requests.Session):
    def __init__(self, **kwargs):
        self.verify = kwargs.pop('verify', False)
        super().__init__(**kwargs)

class AuthorizedJSONDebugSession(AuthorizedSession, JSONSession, DebugSession):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

with AuthorizedJSONDebugSession() as s:
    response = s.post(requestbin_URL, data={"key" : "value"})

dict.pop 方法查找给定的键并将其从字典中删除。第二个参数是要使用的默认值,如果没有提供 KeyError 则被引发。

请注意,必不可少 每当您在子类中定义 __init__ 时,您也会调用 super().__init__(**kwargs),即使您认为在那种情况下不需要。因为将子类添加到具有多个祖先的 类 的层次结构中可以更改 mro,并且如果不执行此调用,您可能会导致初始化失败。