【发布时间】:2022-01-14 17:04:58
【问题描述】:
我想避免在日志中对 f-strings 进行急切的评估,因此我使用 %-formatting。
精度由变量precision定义。
import logging
value = 3.14159
logging.error("%.2f", value)
precision = 2
logging.error(f"{value:.{precision}f}") # OK but eager evaluation
logging.error("%.*f", value, precision) # fails
logging.error("%.%if", value, precision) # fails
有什么建议吗?
【问题讨论】:
-
为什么急切评估在这里是个问题?如果您将格式字符串保存在变量中,这只会是一个问题。
-
@Barmar:我认为他们正在进行过早的优化,并试图避免在日志级别不足以发出字符串时格式化字符串的成本。对于
error级别,这可能毫无意义(您不太可能忽略错误级别消息),但对于闲聊的调试消息,当调试之外的 99% 的使用都不会使用时,避免创建完整字符串可能会有一点好处它。也就是说,我怀疑它在大多数情况下是否重要。 f-strings 变得非常 很快,而日志记录所涉及的其他开销可能超过了避免 temp str 所节省的微不足道的成本。 -
@Barmar 这里没有问题,但遵循使用惰性格式进行日志记录的做法。我使用了
.error,以便可以复制粘贴我的最小代码并直接查看输出(无需额外的行来更改调试级别)。 -
@thomast:很高兴你能想到这一点。我会注意到,在实践中(在使用
logging.basicConfig(filename='/dev/null')以确保输出没有炸毁我的终端之后,刚刚使用 IPython 微基准测试),避免急切格式化的增量好处实际上非常小(尽管显然急切的 printf风格的方法仍然胜过 f 弦)。我将logging.debug('%.*f', precision, value)与logging.debug('%.*f' % (precision, value))进行了比较,lazy 的每次调用成本为 424 ns,而 Eager printf 样式为 682 ns(等效 f-string 为 849 ns)。 -
没错,这里急切格式化的成本是 1.5-2 倍。但是基线成本已经相当高了(所以无论如何你都不想在热循环中做太多事情),并且急切的代码仍然在一个数量级之内(所以在少数情况下你会接受懒惰的开销,但不接受急切的开销)。如果渴望没有为您做任何其他事情,好吧,也许坚持懒惰,但正如我在回答中指出的那样,懒惰评估可以隐藏只有在您打开日志级别以找出问题并发现调试级别日志时才会咬你的错误从来没有工作过。