Python日志中字符串的惰性求值:比较百分号(%)和.format()函数

12

这两个调用之间有什么区别吗:

import logging

logging.getLogger().debug('test: %i' % 42)

logging.getLogger().debug('test: {}'.format(42))

假设当将 42 转换为字符串时,它被替换为一些长时间的计算(比如7.5百万年的计算),得到最终答案为42。

如果日志记录设置为调试模式,第一种方法是否进行了惰性求值?


2
请查看以下链接以了解有关Python字符串格式化的性能问题:https://dev59.com/N1kR5IYBdhLWcg3w3A0M。为了提高性能,您可能需要使用`f-strings`,它们比这些方法更快,请参考:https://dev59.com/EFgQ5IYBdhLWcg3wOBSw。 - rafaelc
非常有用的参考资料,感谢 RafaelC,尽管它没有澄清惰性评估问题。 - David Parks
在您的这两个示例中,格式化字符串在logging模块介入整个过程之前就已被Python完全评估。您可能想将格式化字符串及其参数作为单独的参数传递给.debug(),以便它实际进行格式化 - 我假设仅在消息不会被过滤时才会执行此操作,但我不能百分之百确定。 - jasonharper
1
如果你真的想要懒加载,你需要像这样的东西:if logging.getLogger().isEnabledFor(logging.DEBUG): …。不过根据你对答案的评论,似乎你并不需要那个。 - abarnert
1
或许可以参考 https://dev59.com/-2855IYBdhLWcg3wyneC。解决方案还展示了一个代理对象,以*延迟值生成的成本*。当然,生成调试值仍应该是“相对快速”和“无副作用”的,否则启用调试的行为仍将是有问题/意外的。 - user2864740
另一篇相关文章:https://dev59.com/8V4b5IYBdhLWcg3wbREf - David Parks
2个回答

23

他们都不是懒惰的。在发送到记录器之前,两个字符串都会被插值。Python日志记录中的惰性求值是使用单独的参数完成的。文档https://docs.python.org/2/library/logging.html建议如下进行字符串插值的惰性求值;

logging.getLogger().debug('test: %i', 42)

简述: 在这种情况下,我们发送了一个原始类型(字符串),但只有一个参数传递给记录器。因此它不能懒惰执行。


1
请注意,在Python 3.2+中,您也可以使用str.format样式字符串进行惰性求值(lazy evaluation),尽管%样式仍然是默认值。 - abarnert
这仍然不是所问的lazy:.debug('test: %i', takes_75_million_years_to_generate)它只推迟了字符串本身的创建,而不是作为参数提供的值的生成,这在比较上是无关紧要的:}原始问题含糊不清,带有“我们假设42被一些长计算(例如750万年)替换,产生最终答案42。”的条件,但一旦42产生,成本已经付出了 - user2864740
2
原问题表述不够清晰,是我的错。我只是想询问关于字符串拼接的评估。感谢您澄清了这一点。 - David Parks
我在原问题中添加了“当转换为字符串时”的内容,以澄清这个细节。 - David Parks

5

我在评论中发布的参考文献中查找更详细的关于 %.format() 的信息。

对于“惰性求值”的问题,答案是

只需进行简单的测试即可。

def func1(x):
    time.sleep(5)
    return(x)

def func2(x):
    #time.sleep(5)
    return(x)

%timeit 'debug1: %s' % func1(3)
5 s ± 1.31 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit 'debug2: {}'.format(func1(3))
5 s ± 1.45 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit 'debug1: %s' % func2(3)
297 ns ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit 'debug2: {}'.format(func2(3))
404 ns ± 4.56 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

在使用.format%的方法中,func()总是会被计算。

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