为什么repr(int)比str(int)更快?

34

我想知道为什么repr(int)str(int)更快。以下是代码片段:

ROUNDS = 10000

def concat_strings_str():
    return ''.join(map(str, range(ROUNDS)))

def concat_strings_repr():
    return ''.join(map(repr, range(ROUNDS)))

%timeit concat_strings_str()
%timeit concat_strings_repr()

我得到了这些时间(使用Python 3.5.2,但使用2.7.12也会得到非常相似的结果):

 1.9 ms ± 17.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
 1.38 ms ± 9.07 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

如果我走在正确的道路上,底层正在调用相同的函数long_to_decimal_string

我是不是做错了什么,或者还有其他我所忽略的事情?


更新: 这可能与int__repr____str__方法无关,而与repr()str()之间的差异有关,因为实际上int.__str__int.__repr__是相当快的:

def concat_strings_str():
    return ''.join([one.__str__() for one in range(ROUNDS)])

def concat_strings_repr():
    return ''.join([one.__repr__() for one in range(ROUNDS)])

%timeit concat_strings_str()
%timeit concat_strings_repr()

结果为:

2.02 ms ± 24.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.05 ms ± 7.07 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

@gbtimmon 顺序没有影响。 - Christian Geier
我能够可靠地在OS X上使用python3 -m timeit“''.join(map(repr, range(10000)))”和等效的str版本或多次运行进行复现。这应该消除大部分关于缓存等方面的担忧。但是,如果我去掉join,差异就会消失。python3 -m timeit“map(str, range(10000))” - Rob Napier
2
没有 joinrepr(或 str)实际上不会被调用。@RobNapier - Christian Geier
6
在Python3中,map返回一个迭代器,因此这段代码实际上并没有做任何事情——你需要构建一个列表,比如 python3 -m timeit "list(map(str, range(10000)))" - AChampion
如果Python2的速度相当,那么这是否与Python3中的Unicode字符串有关呢? - Simon Fraser
显示剩余2条评论
3个回答

35
因为使用str(obj)必须先经过type.__call__,然后str.__new__(创建一个新字符串),然后PyObject_Str(将对象转换为字符串),它调用int.__str__,最后使用你链接的函数。 repr(obj)对应于builtin_repr,直接调用PyObject_Repr(获取对象的repr),然后调用int.__repr__,它使用与int.__str__相同的函数。
此外,它们通过call_function(处理调用生成的CALL_FUNCTION opcode的函数)所采取的路径略有不同。
来自GitHub主分支(CPython 3.7): 根据您的更新,这不是关于int.__repr__int.__str__的区别,毕竟它们是相同的函数;而是关于reprstr如何调用它们。str只需要多努力一点。

12
我刚刚比较了3.5分支中的 strrepr 实现。请参见这里str 中似乎有更多的检查:enter image description here


8
有几种可能原因是因为负责返回 strrepr 的CPython函数略有不同。但我想主要原因是因为 str 是一个 type(类),而str.__new__ 方法必须调用 __str__,而 repr 可以直接使用 __repr__

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