如何键入可变默认参数
How to type mutable default arguments
在Python中处理可变默认参数的方法是set them to None。
例如:
def foo(bar=None):
bar = [] if bar is None else bar
return sorted(bar)
如果我输入函数定义,那么 bar
的唯一类型表示 bar
是 Optional
,而显然,它不是 Optional
我希望 运行 那个 sorted
函数在它上面的时间:
def foo(bar: Optional[List[int]]=None):
bar = [] if bar is None else bar
return sorted(bar) # bar cannot be `None` here
那我应该投吗?
def foo(bar: Optional[List[int]]=None):
bar = [] if bar is None else bar
bar = cast(List[int], bar) # make it explicit that `bar` cannot be `None`
return sorted(bar)
我是否应该只希望通读函数的人看到处理默认可变参数的标准模式,并理解对于函数的其余部分,参数不应该是 Optional
?
处理此问题的最佳方法是什么?
编辑:
澄清一下,此函数的用户应该能够将 foo
调用为 foo()
、foo(None)
和 foo(bar=None)
。 (我不认为以任何其他方式拥有它是有意义的。)
编辑#2:
Mypy 会 run with no errors if you never type bar
as Optional
and instead only type it as List[int]
, despite the default value being None
. However, this is highly not recommended because this behavior may change in the future, and it also implicitly types the parameter as Optional
. (See 了解详情。)
为什么不在影子bar
:
时剪掉演员表
def foo(bar: Optional[List[int]]=None):
bar : List[int] = [] if bar is None else bar
return sorted(bar)
如果您将默认值添加到函数中的参数而不是它是可选的。
同样在您的代码中,您允许调用者不向该函数提供任何内容,它仍然有效,因为它只是创建一个空列表。
同样在 python 中,即使键入它也不会强制执行类型。这就是它在文档中称为“类型提示”的原因。
所以如果你想让调用者调用不带参数的函数并且它只是对一个空列表进行排序那么你的代码是正确的。
def foo(bar=None):
bar = [] if bar is None else bar
return sorted(bar)
但是如果你永远不希望调用者不提供任何东西,甚至 None
,那么你应该改变你的函数签名。
def foo(bar: List[int]):
bar = [] if bar is None else bar
return sorted(bar)
现在类型提示检测到 bar
是 List[int]
。你必须传递一些东西给 foo
。所以你可以做 foo(None)
这就是为什么你需要 None
检查但现在 foo()
无效并抛出错误。
如果您确实希望他们不传递任何内容,那么只需执行此操作,类型提示仍然有效。
def foo(bar: List[int]=None):
bar = [] if bar is None else bar
return sorted(bar)
None
不是唯一可用的哨兵。您可以选择 自己的 列表值用作标记,在 运行 时用新的空列表替换它(而不是 None
)。
_sentinel = []
def foo(bar: List[int]=_sentinel):
bar = [] if bar is _sentinel else bar
return sorted(bar)
只要没有人使用 _sentinel
作为显式参数调用 foo
,bar
将始终得到一个新的空列表。在像 foo([])
这样的调用中,bar is _sentinel
将为假:两个空列表不是同一个对象,因为列表的可变性意味着你不能有一个总是被 [= 引用的空列表18=].
我不确定这里有什么问题,因为在 mypy 中使用 Optional[List[int]]
作为类型非常好:https://mypy-play.net/?mypy=latest&python=3.9&gist=2ee728ee903cbd0adea144ce66efe3ab
在你的例子中,当 mypy 看到 bar = [] if bar is None else bar
时,它足够聪明地意识到 bar
不能超过这一点 None
,从而将类型缩小到 List[int]
.在此处阅读有关 mypy 中类型缩小的更多信息:https://mypy.readthedocs.io/en/stable/kinds_of_types.html?highlight=narrow#union-types
这里是 some other examples 类型的缩小:
from typing import *
a: Optional[int]
assert a is not None
reveal_type(a) # builtins.int
b: Union[int, float, str]
if isinstance(b, int):
reveal_type(b) # builtins.int
else:
reveal_type(b) # Union[builtins.float, builtins.str]
使用 Sequence[int]
怎么样?将对参数(包括其默认值)几乎 readonly.
进行类型检查
from collections.abc import Sequence
def foo(bar: Sequence[int] = []) -> list[int]:
return sorted(bar)
还有bar
可以如下变异,但是风险不会很高。 (_sentinel: list[int] = []
也可以变异。)
def foo(bar: Sequence[int] = []) -> list[int]:
if isinstance(bar, list):
# reveal_type(bar) # => Revealed type is "builtins.list[Any]"
bar.append(0)
return sorted(bar)
在Python中处理可变默认参数的方法是set them to None。
例如:
def foo(bar=None):
bar = [] if bar is None else bar
return sorted(bar)
如果我输入函数定义,那么 bar
的唯一类型表示 bar
是 Optional
,而显然,它不是 Optional
我希望 运行 那个 sorted
函数在它上面的时间:
def foo(bar: Optional[List[int]]=None):
bar = [] if bar is None else bar
return sorted(bar) # bar cannot be `None` here
那我应该投吗?
def foo(bar: Optional[List[int]]=None):
bar = [] if bar is None else bar
bar = cast(List[int], bar) # make it explicit that `bar` cannot be `None`
return sorted(bar)
我是否应该只希望通读函数的人看到处理默认可变参数的标准模式,并理解对于函数的其余部分,参数不应该是 Optional
?
处理此问题的最佳方法是什么?
编辑:
澄清一下,此函数的用户应该能够将 foo
调用为 foo()
、foo(None)
和 foo(bar=None)
。 (我不认为以任何其他方式拥有它是有意义的。)
编辑#2:
Mypy 会 run with no errors if you never type bar
as Optional
and instead only type it as List[int]
, despite the default value being None
. However, this is highly not recommended because this behavior may change in the future, and it also implicitly types the parameter as Optional
. (See
为什么不在影子bar
:
def foo(bar: Optional[List[int]]=None):
bar : List[int] = [] if bar is None else bar
return sorted(bar)
如果您将默认值添加到函数中的参数而不是它是可选的。 同样在您的代码中,您允许调用者不向该函数提供任何内容,它仍然有效,因为它只是创建一个空列表。
同样在 python 中,即使键入它也不会强制执行类型。这就是它在文档中称为“类型提示”的原因。
所以如果你想让调用者调用不带参数的函数并且它只是对一个空列表进行排序那么你的代码是正确的。
def foo(bar=None):
bar = [] if bar is None else bar
return sorted(bar)
但是如果你永远不希望调用者不提供任何东西,甚至 None
,那么你应该改变你的函数签名。
def foo(bar: List[int]):
bar = [] if bar is None else bar
return sorted(bar)
现在类型提示检测到 bar
是 List[int]
。你必须传递一些东西给 foo
。所以你可以做 foo(None)
这就是为什么你需要 None
检查但现在 foo()
无效并抛出错误。
如果您确实希望他们不传递任何内容,那么只需执行此操作,类型提示仍然有效。
def foo(bar: List[int]=None):
bar = [] if bar is None else bar
return sorted(bar)
None
不是唯一可用的哨兵。您可以选择 自己的 列表值用作标记,在 运行 时用新的空列表替换它(而不是 None
)。
_sentinel = []
def foo(bar: List[int]=_sentinel):
bar = [] if bar is _sentinel else bar
return sorted(bar)
只要没有人使用 _sentinel
作为显式参数调用 foo
,bar
将始终得到一个新的空列表。在像 foo([])
这样的调用中,bar is _sentinel
将为假:两个空列表不是同一个对象,因为列表的可变性意味着你不能有一个总是被 [= 引用的空列表18=].
我不确定这里有什么问题,因为在 mypy 中使用 Optional[List[int]]
作为类型非常好:https://mypy-play.net/?mypy=latest&python=3.9&gist=2ee728ee903cbd0adea144ce66efe3ab
在你的例子中,当 mypy 看到 bar = [] if bar is None else bar
时,它足够聪明地意识到 bar
不能超过这一点 None
,从而将类型缩小到 List[int]
.在此处阅读有关 mypy 中类型缩小的更多信息:https://mypy.readthedocs.io/en/stable/kinds_of_types.html?highlight=narrow#union-types
这里是 some other examples 类型的缩小:
from typing import *
a: Optional[int]
assert a is not None
reveal_type(a) # builtins.int
b: Union[int, float, str]
if isinstance(b, int):
reveal_type(b) # builtins.int
else:
reveal_type(b) # Union[builtins.float, builtins.str]
使用 Sequence[int]
怎么样?将对参数(包括其默认值)几乎 readonly.
from collections.abc import Sequence
def foo(bar: Sequence[int] = []) -> list[int]:
return sorted(bar)
还有bar
可以如下变异,但是风险不会很高。 (_sentinel: list[int] = []
也可以变异。)
def foo(bar: Sequence[int] = []) -> list[int]:
if isinstance(bar, list):
# reveal_type(bar) # => Revealed type is "builtins.list[Any]"
bar.append(0)
return sorted(bar)