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

'{0}'.format() is faster than str() and '{}'.format() using IPython %timeit and otherwise using pure Python

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

Called by str(object) and the built-in functions format() and print() to compute the “informal” or nicely printable string representation of an object.

所以,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 的首选方法是什么?

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

无论哪种方式,"{}""{pos}" 快一点,后者比 "{name}" 快一点,而它们都比 str 慢。

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

__str__

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

Changed in version 3.1: The positional argument specifiers can be omitted, so '{} {}' is equivalent to '{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,由于其通用性,它会产生一些额外的开销。