为什么`str.format()`会忽略额外/未使用的参数?

17

我看到了“为什么join()不会自动将其参数转换为字符串?”,并且被接受的答案让我想到:

显式优于隐式。

错误不应该悄悄地经过。

那么,为什么str.format()会忽略附加/未使用的(有时是意外传递的)参数?对我来说,这似乎是一个悄悄传递的错误,而且它肯定不是显式的:

>>> 'abc'.format(21, 3, 'abc', object(), x=5, y=[1, 2, 3])
'abc'

这实际上导致我的朋友使用os.makedirs(path, exist_ok=True)时仍然会出现错误,即使 os.makedirs()的文档说即使path已经存在,exist_ok=True也不会引发错误。

事实证明,他只是有一条很长的包含嵌套函数调用的代码行,并且exist_ok被传递到了嵌套的 .format() 调用中,而不是os.makedirs()


3
你希望像这样简单的东西失败吗:'{6} {3}'.format(*range(100))?或者是:'Hi {name} your favourite colour is {colour}'.format(name='Bob', colour='blue', age=21, planet='Earth') - Jon Clements
2
@JonClements 实际上是的。如果字符串需要 namecolour,我认为将 ageplanet 传递给它是一个错误。正如提到的那样,“错误不应该悄悄地通过”。错误也会强制你明确你的语句。 - Markus Meskanen
8
所以,如果我有一个名为person的字典,我要基于其中的某些元素简单地打印一个字符串,那么我应该负责子集化该字典以匹配我的格式吗?这是一个疯狂的要求 - 或者你认为人们真的喜欢做.format(name=person['name'], colour=['person'])吗? - Jon Clements
2
根据我从Python之禅中所理解的,你不应该只是“传递”整个字典。如果你想要打印出一个人的名字和年龄,就应该传入这个人的名字和年龄。编写一个函数来接收这个人并打印出他的名字和年龄,以简化主函数。 - Markus Meskanen
7
@MarkusMeskanen你引用了“错误不应该默默通过”。这里真正的问题是,你所描述的场景是否应该被视为错误。我认为不应该。 - DeepSpace
显示剩余3条评论
1个回答

19

忽略未使用的参数使得可以创建任意大小的字典或对象的任意格式字符串。

比如说你想让你的程序具有让最终用户更改输出的功能。你记录那些可用的字段,并告诉用户将这些字段放在字符串中的{...}槽中。最终用户可以创建带有任意数量的这些字段的模板字符串,包括没有被使用的字段,而不会出错

换句话说,这个选择是有意为之的,因为允许转换的参数比实际传入的参数多,具有实际应用价值。需要注意的是,启发Python PEP的C# String.Formatter实现出于同样的原因也是这么做的。

需要注意的是,在PEP的这一部分的讨论不是那么明确;Guido van Rossum在某些时候试图解决这个问题

PEP对于如果有太少或太多的位置参数,或者缺少或未使用关键字参数的情况没有表态。缺少参数应该是错误的;我不确定冗余(未使用)参数的情况。一方面,抱怨这些参数可以让我们更加确定格式字符串是正确的。另一方面,有一些用例需要传递大量的关键字参数(例如简单的Web模板可能使用**dict传递一个固定集合的变量)。甚至在i18n(翻译)应用程序中,我可以看到允许未使用的参数的有用性。

对此,PEP的作者回答说他们还没有就这个问题做出决定。

如果你的使用场景需要因未使用的参数而引发异常,那么你需要继承string.Formatter()并为Formatter.check_unused_args()提供实现;默认实现什么也不做。当然,这对于你使用的str.format(*args, **kwargs)而不是Formatter().format(str, *args, **kwargs)的情况没有帮助。我相信在某个时候,曾经有过想法,即可以用自定义实现替换str.format()所使用的格式化程序,但这从未实现。

如果你使用flake8检查工具,则可以添加flake8-string-format插件来检测明显的情况,例如,你传递了一个显式的关键字参数,但格式化字符串中未使用该参数。


2
参考你在另一个问题上的回答,这启发了我提出这个问题,这种情况有什么不同吗?.format()的行为可能会隐藏错误(就像它在我朋友的案例中所做的那样),而且.format()会忽略你传递给它的一切。在我看来,这甚至比join()更糟糕。 - Markus Meskanen
3
在禅宗中也有一句话:尽管纯粹性很重要,但实用性胜过纯粹性。这里有很好的实用理由不在此处引发异常。我正在浏览原始PEP讨论以找到关于此决定的具体说明。 - Martijn Pieters
4
对我来说,禅宗就像美国法律一样:它包含了所有的东西,你可以通过引用其中的不同部分来证明任何事情。在我看来,“实用性胜过纯洁性”这个观点也适用于另一个问题,即str.join()应该将所有内容转换为字符串,因为这只是实际的做法。如果你用连字符连接12,它还能做什么,除了得到'1-2'。虽然这是另一个问题需要讨论的内容... - Markus Meskanen
3
禅(Zen)是一种指引,而非法律。更好的解释是,它最初只是一个玩笑。它是一种哲学,而非约束性文件。 - Martijn Pieters
4
你可以自由地向Python核心开发人员提出这个问题。 - Martijn Pieters
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接