在添加类型提示时遇到的问题
问题现已编辑为仅讨论一个错误,因为管理员因包含太多错误而对其进行了屏蔽。请注意,之所以列出所有错误,是因为它们可能相关:
在PyCharm中解决剩余的类型提示问题时有些困难。
代码:
from wsgiref.types import StartResponse
import importlib.util
import importlib.machinery
interactive_mode = True
if interactive_mode:
from collections.abc import Callable
import logging
from wsgiref import simple_server
class WsgiController:
def __init__(self, environ: dict[str, str], start_response: StartResponse) -> None:
self.oPage = Process(environ, start_response)
httpd = simple_server.make_server("", 8000, WsgiController)
class Request:
def __init__(self, environ: dict[str, str]) -> None:
self.environ = environ
def get(self, key: str) -> str | None:
return self.environ.get(key)
def parse_cookies(self) -> None:
_ = self.get("HTTP_COOKIE")
def set_cookie(self, attributes: None | dict[str, str] = None) -> None:
_ = self
if attributes is None: attributes = {}
for _, _ in attributes.items():
pass
class Response:
start_response: Callable[[str, list[tuple[str, str]]], None]
def __init__(self, start_response) -> None:
self.start_response = start_response
self.headers = [("", "")]
def serve_pre_compiled(self) -> str:
local_dict = {"oRequest": Process.oRequest, "oResponse": self}
return local_dict["__HTML__"]
def serve_new_source(self, source_file: str, module_name: str, pyc_file: str) -> str:
spec = importlib.util.spec_from_file_location(module_name, source_file)
if spec is not None and spec.loader is not None:
importlib.util.module_from_spec(spec)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
_ = self
_ = pyc_file
return _
class Process:
oRequest: Request
oResponse: Response
logger: logging.Logger
def __init__(self, environ, start_response: StartResponse) -> None:
_ = self.oResponse.start_response.__self__.status
Process.oRequest = Request(environ)
Process.oResponse = Response(start_response)
下一个错误:
Member 'None' of 'dict[str, str] | None' does not have attribute 'items'
for _, _ in attributes.items():
解决方案
- 在你的代码中你说你有:
py def set_cookie(self, attributes: None | dict[str, str] = None) -> None: _ = self if attributes is None: attributes = {} for _, _ in attributes.items(): pass
关于你提到的错误:
console Member 'None' of 'dict[str, str] | None' does not have attribute 'items' for _, _ in attributes.items():
该错误表示你的类型提示允许它具有 type == None,这与 type == type(None) 不同,因此如果 type = None,错误的含义是:hasattr(type, "items") is False
不过,这个问题因用局部变量 attributes 覆盖参数而有点复杂(从技术上讲并非错误,只是在阅读和调试时会显得有点误导)
正如 @Hayden T. Brown 的注释所提,你应该尝试像下面这样:
_attributes: dict[str, str] = attributes if attributes else {}
完整函数(已修补):
def set_cookie(self, attributes: None | dict[str, str]) -> None:
# if you must use 'self' to make a poorly tuned linter happy try this:
if not self: return None # e.g., must never happen
# keep the local var _attributes as a strict type dict[str, str]
_attributes: dict[str, str] = attributes if attributes else {}
for _, _ in _attributes.items():
pass
- 事后改进:
你也可以在 def 表达式中使用 {} 的默认值;例如:
def set_cookie(self, attributes: dict[str, str] = {}) -> None:
if not self: return None # e.g., must never happen
_attributes: dict[str, str] = attributes if attributes else {}
for _, _ in _attributes.items():
pass
这将使 def 的意图与类型提示与语义行为保持一致。需要注意的是,用显式的 None 调用 some_request.set_cookie(None) 在类型提示上被视为对你代码的API违规(尽管实现仍然会处理),并可能被lint工具标记。
- 为了减少lint的噪音,来自评论中的讨论(当你不想使用Python的默认可变参数时)
你甚至可以在此时从 def 表达式中省略默认值;例如:
def set_cookie(self, attributes: dict[str, str]) -> None:
if not self: return None # e.g., must never happen
_attributes: dict[str, str] = attributes if attributes else {}
for _, _ in _attributes.items():
pass
然而,通过省略默认参数,以及一个 None-类型的类型提示,调用方每次都需要传入一个 dict[str, str],以便与你的类型提示对齐(请参考前面的第一种做法作为推荐);例如,需要 some_request.set_cookie(attributes={}) 而不是 some_request.set_cookie()(尽管Python运行时当然仍然接受两者,因为类型提示是给开发者看的,而不是给运行时用的)
另请参阅官方文档以解释这种误导性错误:
- https://docs.python.org/3/library/stdtypes.html#dict.items
- https://docs.python.org/3/library/functions.html#hasattr
相关问题: