簡體   English   中英

格斗python類型注解

[英]Fighting python type annotations

我有一個非常簡單的 class 繼承自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)

我很難讓mypyrequest方法感到滿意。

第一個挑戰是獲取要驗證的參數; 設置url: Union[str, bytes]是匹配types-requests中的類型注釋所必需的。 我剛剛舉手使*args**kwargs正確,因為唯一的解決方案似乎是重現單個參數注釋,但我很高興將其保留。

處理完 function 簽名后, mypy現在抱怨對startswith的調用:

example.py:23:錯誤:“bytes”的“startswith”的參數 1 具有不兼容的類型“str”; 預期“聯合[字節,元組[字節,...]]”

我可以通過明確的cast來解決這個問題:

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

...但這似乎只是引入了復雜性。

然后對urllib.parse.urljoin的調用不滿意:

example.py:24:錯誤:“urljoin”的類型變量“AnyStr”的值不能是“Sequence[object]”
example.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)

但這感覺像是一個 hacky 解決方法。

所以:

  • 我想我的 function 簽名和我想得到的一樣正確,但是url上的類型注釋是正確的還是不正確並導致問題?

  • urljoin周圍的錯誤是怎么回事?


從評論中可以看出:

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

失敗:

example.py:25:錯誤:“str”的“startswith”的參數 1 具有不兼容的類型“Union[str, bytes]”; 預期“聯合[str,元組[str,...]]”
example.py:25:錯誤:“bytes”的“startswith”的參數 1 具有不兼容的類型“Union [str, bytes]”; 預期“聯合[字節,元組[字節,...]]”

這解決了整個問題:

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 如果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時獲取bytes ,反之亦然,它仍然無法驗證。 mypy無法通過運行時邏輯進行驗證,因此您只能執行以下操作:

    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)

它支持兩者,但以丑陋的方式重復邏輯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM