使用 typeVars 和可调用参数时,Python (mypy) 类型不兼容

问题描述 投票:0回答:1

我目前正在尝试创建一个类型化的请求包装器,并允许您提供响应处理程序。此响应处理程序可能会更改 http 请求的返回值。

不幸的是,我无法克服一些打字问题(我对使用 python 打字还很陌生)。这对我来说似乎是一个普遍问题或误解,这就是我创建这篇文章的原因。我已经阅读了几个概念,如泛型或协议,但我无法创建一个可行的解决方案,我也不认为我完全理解哪种解决方案可能是最好的。

这是我的示例代码:

from collections.abc import Callable
from typing import TypeVar

from requests import Response

_T = TypeVar("_T", default=Response)


def identity(response: Response) -> Response:
    return response


def extract_text(response: Response) -> str:
    return response.text


class HTTPRequestsAPI:

    def fake_request(self, response_handler: Callable[[Response], _T] = identity) -> _T:
        response = Response()
        processed_response = response_handler(response)
        return processed_response


if __name__ == "__main__":
    request_api = HTTPRequestsAPI()
    resp = request_api.fake_request()
    print(type(resp))

    resp_text = request_api.fake_request(response_handler=extract_text)
    print(type(resp_text))

我可以毫无问题地运行主程序并获得以下输出(类型),这符合预期:

<class 'requests.models.Response'>
<class 'str'>

当我尝试运行 mypy 时,出现错误,我不明白为什么会发生这种情况,因为我认为应该正确推断类型:

error: Incompatible default for argument "response_handler" (default has type "Callable[[Response], Response]", argument has type "Callable[[Response], _T]")  [assignment]

我没有得到什么,这可能是与 mypy 相关的问题吗?我还没有尝试过像“pyright”这样的另一种类型检查器。非常感谢您的帮助!

**我在此示例中使用 python = 3.13 和 mypy = 1.13.0。我也尝试过不使用 TypeVar 的“默认值”,但这没有什么区别,我认为默认值可能会对打字机有所帮助。

python python-typing mypy pyright
1个回答
0
投票

正如

评论
中的@Gaberocksall所述,上述问题是mypy特有的(参见https://github.com/python/mypy/issues/3737)。一个选择是坚持使用 mypy 并使用
typing.overload

我创建了一个实现相同行为但被 mypy 接受的示例:

from collections.abc import Callable
from typing import TypeVar, overload

from requests import Response

_T = TypeVar("_T", default=Response)


def identity(response: Response) -> Response:
    return response


def extract_text(response: Response) -> str:
    return response.text


class HTTPRequestsAPI:

    @overload
    def fake_request(self, response_handler: Callable[[Response], _T]) -> _T:
        ...

    @overload
    def fake_request(self) -> Response:
        ...

    def fake_request(self, response_handler=identity):
        response = Response()
        processed_response = response_handler(response)
        return processed_response


if __name__ == "__main__":
    request_api = HTTPRequestsAPI()
    resp = request_api.fake_request()
    print(type(resp))

    resp_text = request_api.fake_request(response_handler=extract_text)
    print(type(resp_text))

另一方面,

@InSync
评论中提到,一个可能的解决方案是切换到pyright,因为pyright将解析发布的代码而没有任何错误(我可以验证这一点)。

总之,这可能是个人偏好,但我会改用pyright以避免“不必要的”超载。在我的“真实”项目中,这会破坏代码库,并使其在我看来变得非常不可读。

非常感谢评论中提供的帮助!

© www.soinside.com 2019 - 2024. All rights reserved.