参数 2 到 "join" 具有不兼容的类型 "Optional[str]" ;预期"str"



我正在运行myypy预提交钩子来检查任何可能的类型问题,它一直给我这个错误Argument 2 to "join" has incompatible type "Optional[str]"; expected "str"以下代码:

else:
renamed_paths_dict: CustomConnectorRenameDict = {
"old_path": os.path.join(
self.temp_dir, change["file_path"]
),
"new_path": os.path.join(
self.temp_dir,
change["new_file_path"], -> this is the line mypy is talking about
),
}

change["new_file_path"]可以是字符串或None,但在这个特定的else块中,它永远不会是None

如何解决这个问题?

感谢

请允许我以这样一种方式重写你的问题:给出一个适当的最小可重复的例子,扔掉所有不相关的东西(与实际问题无关),只保留要点。

如果字典中的值是str | None类型,但我确定其中一个绝对是str(而不是None),我如何告诉静态类型检查器?下面的代码使用mypy产生一个错误:

import os

temp_dir = "tmp"
paths: dict[str, str | None] = {}
...
paths["new_file_path"] = "foo"
...
new_path = os.path.join(temp_dir, paths["new_file_path"])

错误:

参数2 "join"有不兼容的类型"Optional[str]";期望的"str" [arg-type]

答案

告诉类型检查器期望与键"new_file_path"对应的值是str:

...
paths["new_file_path"] = "foo"
...
assert paths["new_file_path"] is not None
new_path = os.path.join(temp_dir, paths["new_file_path"])

另外:

...
assert isinstance(paths["new_file_path"], str)
new_path = os.path.join(temp_dir, paths["new_file_path"])

如果你不想写额外的类型保护,你总是可以使用type: ignore,但你应该总是尝试通过使用正确的错误代码来沉默,使那些尽可能窄:

new_path = os.path.join(temp_dir, paths["new_file_path"])  # type: ignore[arg-type]

但我不会走那条路。断言还有一个额外的好处,如果您在某个地方犯了错误,而new_file_path的值恰好是None,则可以立即给出一个干净且明显的错误。

我也会绝对不会采用paths["new_file_path"] or "some string"短路的路线。这甚至更危险,因为它可能会在您的代码中引入无声的bug,因为您说您期望new_file_path值是字符串。如果你犯了一个错误,代码会给你一个tmp/some string的路径,而不会引发一个错误。

p

感谢@SUTerliakov指出关于特定字典值的断言并不完全安全。如果您想要真正精确和安全,您应该为此使用一个中间变量:

...
new_file_path = paths["new_file_path"]
assert new_file_path is not None  # isinstance(new_file_path, str)
new_path = os.path.join(temp_dir, new_file_path)
为了完整起见,您还可以这样使用typing.cast:
from typing import cast
...
new_path = os.path.join(temp_dir, cast(str, paths["new_file_path"]))

但是这基本上和一个合适的和特定的type: ignore有相同的效果,所以我仍然推荐assert

你有多个选择:

  • 通过添加注释# type: ignore来忽略来自myypy的错误:
else:
renamed_paths_dict: CustomConnectorRenameDict = {
"old_path": os.path.join(
self.temp_dir, change["file_path"]
),
"new_path": os.path.join(
self.temp_dir,
change["new_file_path"],  # type: ignore
),
}
  • 给变量一个默认值:
else:
renamed_paths_dict: CustomConnectorRenameDict = {
"old_path": os.path.join(
self.temp_dir, change["file_path"]
),
"new_path": os.path.join(
self.temp_dir,
change["new_file_path"] or "",
),
}
  • else语句的开头添加一个断言(如果你使用它,将会带来一个强盗的警告):
else:
assert change["new_file_path"] is not None
renamed_paths_dict: CustomConnectorRenameDict = {
"old_path": os.path.join(
self.temp_dir, change["file_path"]
),
"new_path": os.path.join(
self.temp_dir,
change["new_file_path"],
),
}

最新更新