为什么 repr(int) 比 str(int) 快?
Why is repr(int) faster than str(int)?
我想知道为什么 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)
如果我在正确的道路上,the same function long_to_decimal_string
is getting called 幕后。
我是不是弄错了什么或者我还遗漏了什么?
更新:
这可能与 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)
有几种可能性,因为负责 str
and repr
return 的 CPython 函数略有不同。
但我猜主要原因是str
是一个type
(一个class)而str.__new__
method has to call __str__
而repr
可以直接去__repr__
.
我刚刚比较了 3.5 分支中的 str
和 repr
实现。
参见 here。
str
中似乎有更多检查:
因为使用 str(obj)
必须首先经过 type.__call__
然后 str.__new__
(create a new string) then PyObject_Str
(make a string out of the object) which invokes int.__str__
并且 最后 使用您链接的函数。
repr(obj)
,对应builtin_repr
,直接调用PyObject_Repr
(get the object repr) which then calls int.__repr__
,与int.__str__
.
使用相同的功能
此外,他们通过 call_function
(the function that handles the CALL_FUNCTION
opcode 为调用生成的路径)略有 不同。
来自 GitHub (CPython 3.7) 上的主分支:
str
经过 _PyObject_FastCallKeywords
(which is the one that calls type.__call__
). Apart from performing more checks, this also needs to create a tuple to hold the positional arguments (see _PyStack_AsTuple
)。
repr
经过 _PyCFunction_FastCallKeywords
which calls _PyMethodDef_RawFastCallKeywords
. repr
is also lucky because, since it only accepts a single argument (the switch leads it to the METH_0
case in _PyMethodDef_RawFastCallKeywords
) there's no need to create a tuple, just indexing of the args。
如您的更新所述,这与 int.__repr__
和 int.__str__
无关,毕竟它们是相同的功能;这完全是关于 repr
和 str
如何到达他们。 str
只是需要再努力一点。
我想知道为什么 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)
如果我在正确的道路上,the same function long_to_decimal_string
is getting called 幕后。
我是不是弄错了什么或者我还遗漏了什么?
更新:
这可能与 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)
有几种可能性,因为负责 str
and repr
return 的 CPython 函数略有不同。
但我猜主要原因是str
是一个type
(一个class)而str.__new__
method has to call __str__
而repr
可以直接去__repr__
.
我刚刚比较了 3.5 分支中的 str
和 repr
实现。
参见 here。
str
中似乎有更多检查:
因为使用 str(obj)
必须首先经过 type.__call__
然后 str.__new__
(create a new string) then PyObject_Str
(make a string out of the object) which invokes int.__str__
并且 最后 使用您链接的函数。
repr(obj)
,对应builtin_repr
,直接调用PyObject_Repr
(get the object repr) which then calls int.__repr__
,与int.__str__
.
此外,他们通过 call_function
(the function that handles the CALL_FUNCTION
opcode 为调用生成的路径)略有 不同。
来自 GitHub (CPython 3.7) 上的主分支:
str
经过_PyObject_FastCallKeywords
(which is the one that callstype.__call__
). Apart from performing more checks, this also needs to create a tuple to hold the positional arguments (see_PyStack_AsTuple
)。repr
经过_PyCFunction_FastCallKeywords
which calls_PyMethodDef_RawFastCallKeywords
.repr
is also lucky because, since it only accepts a single argument (the switch leads it to theMETH_0
case in_PyMethodDef_RawFastCallKeywords
) there's no need to create a tuple, just indexing of the args。
如您的更新所述,这与 int.__repr__
和 int.__str__
无关,毕竟它们是相同的功能;这完全是关于 repr
和 str
如何到达他们。 str
只是需要再努力一点。