如果可变的默认参数被注释为不可变,是否可以使用?

Is it OK to have a mutable default argument if it's annotated as immutable?

提问人:Hyperplane 提问时间:9/1/2023 最后编辑:wjandreaHyperplane 更新时间:9/1/2023 访问量:78

问:

通常,有充分的理由,在 python 中使用可变的默认参数被认为是不安全的。另一方面,总是必须把所有东西都包起来,并在功能开始时做小小的开箱舞,这是很烦人的。Optional

在想要允许传递给子函数的情况下,似乎还有另一种选择:**kwargs

def foo(
    x: int,
    subfunc_args: Sequence[Any] = (),
    subfunc_kwargs: Mapping[str, Any] = {},
) -> R:
    ...
    subfunc(*subfunc_args, **subfunc_kwargs)

显然,是一个可变的默认参数,因此被认为是不安全的。但是,由于被注释为 ,而不是 或 ,如果我们最终发生突变,类型检查器将引发错误。{}subfunc_kwargsMappingdictMutableMapping

问题是:这是否被认为是可以做到的,还是仍然是一个可怕的想法?

不必跳小舞和有更整洁的签名,那就太好了。subfunc_kwargs = {} if subfunc_kwargs is None else subfunc_kwargs

注意:不是一个选项,因为这可能会与其他键发生冲突,如果更改 kwargs 会导致问题。**subfunc_kwargssubfunc

python 默认参数

评论

0赞 Barmar 9/1/2023
如果你只是使用字典来表达kwargs,它通常不会发生变异。因此,只需以正常方式默认它,您就不需要“开箱舞”。
1赞 Mark Ransom 9/1/2023
如果你正在寻找一个强大的类型系统来保护你自己,Python 是一个糟糕的选择。
0赞 Karl Knechtel 9/1/2023
类型批注除了分配某些属性外,对代码在运行时的行为没有影响。它们只是使用第三方类型检查器的工具,它只能禁止您运行代码,但不能使其失效。这不是一个合适的问题,因为它归结为“如果你实际上不改变它,那么有一个可变的默认参数是'可以'的吗?这是模棱两可的;要么你的意思是“问题是否会像书面的那样发生”(显然不是),要么你想到了一些主观的代码质量准则(我们不这样做)。__annotations__

答:

2赞 wjandrea 9/1/2023 #1

你不需要首先把自己放在这种情况中,因为你依赖于类型检查器,当你可以放置一个实际的不可变默认参数时:

from types import MappingProxyType

...
    subfunc_kwargs: Mapping[str, Any] = MappingProxyType({})

请参阅什么是“冻结词典”?

评论

0赞 wjandrea 9/1/2023
我只是突然想到,这并不能完全解决标题中的问题,但我是设置新标题的人,我可能把它弄得太宽泛了。
0赞 Hyperplane 9/1/2023
谢谢,我不知道.PEP603 看起来也是一个非常好的解决方案。MappingProxyType
0赞 GabCaz 9/1/2023 #2

“不用跳小舞,签名更整洁,那就太好了。”subfunc_kwargs = {} if subfunc_kwargs is None else subfunc_kwargs

为什么会这样?在 Python 中,将默认值设置为是一个非常强大的选择,我认为健壮性和清晰度比优雅更可取(在它们发生冲突的情况下)。从某种意义上说,它也非常易于维护,所有开发人员都可以访问它,并且所有静态代码分析器都可以很好地理解它。None

评论

0赞 Hyperplane 9/1/2023
这真的很烦人和重复的样板😩.此外,它可能会损害可读性,因为默认值必须在其他地方解释,而不是简单地在签名中解释。