【问题标题】:'{0}'.format() is faster than str() and '{}'.format() using IPython %timeit and otherwise using pure Python'{0}'.format() 比 str() 和 '{}'.format() 使用 IPython %timeit 更快,否则使用纯 Python
【发布时间】:2016-11-17 13:40:19
【问题描述】:

所以这是一个 CPython 的东西,不太确定它与其他实现有相同的行为。

但是'{0}'.format()str()'{}'.format() 快​​。我发布了 Python 3.5.2 的结果,但是,我用 Python 2.7.12 进行了尝试,趋势是一样的。

%timeit q=['{0}'.format(i) for i in range(100, 100000, 100)]
%timeit q=[str(i) for i in range(100, 100000, 100)]
%timeit q=['{}'.format(i) for i in range(100, 100000, 100)]

1000 loops, best of 3: 231 µs per loop
1000 loops, best of 3: 298 µs per loop
1000 loops, best of 3: 434 µs per loop

来自docsobject.__str__(self)

str(object) 和内置函数format()print() 调用,以计算对象的“非正式”或可良好打印的字符串表示。

那么,str()format() 调用相同的 object.__str__(self) 方法,但是速度上的差异从何而来?

更新 正如@StefanPochmann 和@Leon 在 cmets 中指出的那样,他们得到了不同的结果。我尝试用python -m timeit "..." 运行它,他们是对的,因为结果是:

$ python3 -m timeit "['{0}'.format(i) for i in range(100, 100000, 100)]"
1000 loops, best of 3: 441 usec per loop

$ python3 -m timeit "[str(i) for i in range(100, 100000, 100)]"
1000 loops, best of 3: 297 usec per loop

$ python3 -m timeit "['{}'.format(i) for i in range(100, 100000, 100)]"
1000 loops, best of 3: 420 usec per loop

看来,IPython 正在做一些奇怪的事情……

新问题:通过速度将对象转换为str 的首选方法是什么?

【问题讨论】:

  • 为了更快,您也可以尝试直接使用i.__str__()。虽然str 确实是正确的方法。对你来说还不够快吗?

标签: python cpython


【解决方案1】:

由于某种原因,IPython 计时刚刚关闭(不过,当在不同单元格中使用更长的格式字符串进行测试时,它的表现稍微好一点)。也许在同一个单元格中执行是不对的,不知道。

不管怎样,"{}""{pos}" 快一点,"{name}""{name}" 快,而它们都比 str 慢。

str(val) 是将对象转换为str 的最快方法;它直接调用对象的__str__(如果存在),并返回结果字符串。其他的,如format,(或str.format)由于额外的函数调用(对format本身)而包括额外的开销;处理任何参数,解析格式字符串并然后调用其args__str__

对于str.format 方法"{}" 使用自动编号;来自docs on the format syntax中的一小部分:

3.1 版更改:位置参数说明符可以省略,因此'{} {}' 等价于'{0} {1}'

也就是说,如果您提供以下形式的字符串:

"{}{}{}".format(1, 2, 3)

CPython 立即知道这相当于:

"{0}{1}{2}".format(1, 2, 3)

使用格式字符串,其中包含指示位置的数字; CPython 不能假设一个严格递增的数字(从 0 开始),并且必须解析每个括号以使其正确,从而在此过程中减慢一点速度:

"{1}{2}{0}".format(1, 2, 3)

这就是为什么也不允许将这两者混合在一起的原因:

"{1}{}{2}".format(1, 2, 3)

当你尝试这样做时,你会得到一个很好的ValueError

ValueError: cannot switch from automatic field numbering to manual field specification

它还抓住了这些positionals with PySequence_GetItem,至少与PyObject_GetItem相比,我敢肯定它很快[见下]。

对于"{name}" 值,CPython 总是有额外的工作要做,因为我们处理的是关键字参数而不是位置参数;这包括为调用构建字典和生成更多LOAD 字节码指令以加载keys 和值。函数调用的关键字形式总是引入一些开销。此外,似乎抓取实际上使用了PyObject_GetItem,由于其通用性,这会产生一些额外的开销。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 2021-11-27
    • 2017-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多