'{0}'。format()比使用IPython%timeit的str()和'{}'。format()更快,否则使用纯Python

时间:2016-11-17 13:40:20

标签: python cpython

所以这是一个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

object.__str__(self)上的docs

  

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

因此,str()format()调用相同的object.__str__(self)方法,但速度的差异来自哪里?

更新 正如@StefanPochmann和@Leon在评论中指出的那样,他们会得到不同的结果。我尝试使用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的首选方法?

1 个答案:

答案 0 :(得分:7)

由于某些原因,IPython时序刚刚关闭(但是,当在不同的单元格中使用较长格式的字符串进行测试时,它表现得略好)。也许在同一个细胞中执行是不对的,不是真的知道。

无论哪种方式,"{}""{pos}"快一点,速度比"{name}"快,而str慢于str(val)

str是将对象转换为__str__的最快方法;它直接调用对象' format,如果存在,则返回结果字符串。其他的,如str.format,(或format)包含额外的开销,因为额外的函数调用(到__str__本身);处理任何参数,解析格式字符串,然后然后调用args的{​​{1}}。

对于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和值生成更多key字节代码指令的方法。函数调用的关键字形式总是会引入一些开销。此外,似乎抓取实际上使用了PyObject_GetItem,由于其通用性质,会产生一些额外的开销。