社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

对抗python类型注释

larsks • 3 年前 • 1308 次点击  

我有一个非常简单的类继承自 requests.Session .代码当前看起来像:

import requests
import urllib.parse

from typing import Any, Optional, Union, cast

default_gutendex_baseurl = "https://gutendex.com/"


class Gutendex(requests.Session):
    def __init__(self, baseurl: Optional[str] = None):
        super().__init__()
        self.baseurl = baseurl or default_gutendex_baseurl

    def search(self, keywords: str) -> Any:
        res = self.get("/books", params={"search": keywords})
        res.raise_for_status()
        return res.json()

    def request(
        self, method: str, url: Union[str, bytes], *args, **kwargs
    ) -> requests.Response:
        if self.baseurl and not url.startswith("http"):
            url = urllib.parse.urljoin(self.baseurl, url)

        return super().request(method, url, *args, **kwargs)

我很难做出正确的决定 mypy 满意 request 方法

第一个挑战是获得要验证的参数;背景 url: Union[str, bytes] 是匹配中的类型批注所必需的 types-requests 1.我刚举手示意 *args **kwargs 正确,因为唯一的解决方案似乎是 复制单个参数注释,但我很乐意 就这样吧。

处理函数签名后, 麦皮 她现在在抱怨 关于打电话给 startswith :

实例py:23:错误:“字节”的“startswith”的参数1的类型“str”不兼容;应为“联合[字节,元组[字节,…]”

我可以用一个明确的答案来解决这个问题 cast :

        if not cast(str, url).startswith("http"):
            url = urllib.parse.urljoin(self.baseurl, url)

...但这似乎只是引入了复杂性。

然后,它对打电话给 urllib.parse.urljoin :

实例py:24:错误:“urljoin”的类型变量“AnyStr”的值不能是“Sequence[object]”
实例py:24:错误:赋值中的类型不兼容(表达式的类型为“Sequence[object]”,变量的类型为“Union[str,bytes]”)

我真的不知道该如何看待这些错误。

我现在已经通过将显式演员阵容移动到 方法:

      def request(
          self, method: str, url: Union[str, bytes], *args, **kwargs
      ) -> requests.Response:
          _url = url.decode() if isinstance(url, bytes) else url

          if not _url.startswith("http"):
              _url = urllib.parse.urljoin(self.baseurl, _url)

          return super().request(method, _url, *args, **kwargs)

但这感觉像是一个很难解决的问题。

所以:

  • 我想我的函数签名是正确的 是的,但是上面有类型注释吗 url 对还是错 不正确并导致问题?

  • 周围的错误是怎么回事 urljoin ?


从评论中可以看出:

        if self.baseurl and not url.startswith(
            "http" if isinstance(url, str) else b"http"
        ):

失败于:

实例py:25:错误:“str”的“startswith”的参数1的类型“Union[str,bytes]”不兼容;应为“联合[str,元组[str,…]”
实例py:25:错误:“字节”的“startswith”的参数1的类型“Union[str,bytes]”不兼容;应为“联合[字节,元组[字节,…]”

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/128953
 
1308 次点击  
文章 [ 1 ]  |  最新文章 3 年前
Grismar
Reply   •   1 楼
Grismar    3 年前

这就解决了整个问题:

import requests
import urllib.parse

from typing import Union, cast

default_gutendex_baseurl = "https://gutendex.com/"


class Gutendex(requests.Session):
    def __init__(self, baseurl: str = None):
        super().__init__()
        self.baseurl = baseurl or default_gutendex_baseurl

    def search(self, keywords: str) -> dict[str, str]:
        res = self.get("/books", params={"search": keywords})
        res.raise_for_status()
        return res.json()

    def request(
        self, method: str, url: Union[str, bytes], *args, **kwargs
    ) -> requests.Response:
        if isinstance(url, str):
            if not url.startswith("http"):
                url = urllib.parse.urljoin(self.baseurl, url)

            return super().request(method, url, *args, **kwargs)
        else:
            raise TypeError('Gutendex does not support bytes type url arguments')

你不能不处理 bytes 如果你说你接受它。只要提出一个例外或者做一些更好的事情 字节 通过。甚至只是 pass 如果你喜欢危险的生活。

这段代码在中验证得很好 mypy .

有点令人失望的是,这样的事情并不能证明:

        if not url.startswith("http"):
            url = urllib.parse.urljoin(self.baseurl, url if isinstance(url, str) else url.decode())
        return super().request(method, url, *args, **kwargs)

即使没有办法 url.startswith 得到一个 字节 当它是一个 str 反之亦然,它仍然无法验证。 麦皮 无法通过运行时逻辑进行验证,因此您只能执行以下操作:

    def request(
        self, method: str, url: Union[str, bytes], *args, **kwargs
    ) -> requests.Response:
        if isinstance(url, str):
            if not url.startswith("http"):
                url = urllib.parse.urljoin(self.baseurl, url)

            return super().request(method, url, *args, **kwargs)
        else:
            if not url.startswith(b"http"):
                url = urllib.parse.urljoin(self.baseurl, url.decode())

            return super().request(method, url, *args, **kwargs)

这两者都支持,但以一种丑陋的方式重复了逻辑。