python - ¿Por qué es repr(int) más rápido que str(int)?
performance python-internals (3)
Me pregunto por qué repr(int) es más rápido que str(int) . Con el siguiente fragmento de código:
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()
Obtengo estos tiempos (Python 3.5.2, pero resultados muy similares con 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)
Si estoy en el camino correcto, se llama a la misma función long_to_decimal_string debajo del capó.
¿Obtuve algo mal o qué más está pasando que me estoy perdiendo?
actualización : Esto probablemente no tiene nada que ver con los métodos __repr__ o __str__ int , pero con las diferencias entre repr() y str() , ya que int.__str__ e int.__repr__ son, de hecho, int.__repr__ rápidos:
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()
resultados en:
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)
Acabo de comparar las implementaciones str y repr en la rama 3.5. Mira here .
Debido a que el uso de str(obj) primero debe pasar por type.__call__ then str.__new__ (crea una nueva cadena) luego PyObject_Str ( PyObject_Str una cadena fuera del objeto) que invoca int.__str__ y, finalmente , utiliza la función que has vinculado.
repr(obj) , que corresponde a builtin_repr , llama directamente a PyObject_Repr (obtiene el objeto repr) que luego llama a int.__repr__ que usa la misma función que int.__str__ .
Además, la ruta que toman a través de call_function (la función que maneja el CALL_FUNCTION operación CALL_FUNCTION que se genera para las llamadas) es ligeramente diferente.
Desde la rama principal en GitHub (CPython 3.7):
-
strpasa por_PyObject_FastCallKeywords(que es el que llama altype.__call__). Además de realizar más controles, esto también necesita crear una tupla para contener los argumentos posicionales (ver_PyStack_AsTuple). -
reprpasa por_PyCFunction_FastCallKeywordsque llama_PyMethodDef_RawFastCallKeywords.reprtambién tiene suerte porque, dado que solo acepta un único argumento (el cambio lo lleva al caso_PyMethodDef_RawFastCallKeywordsen_PyMethodDef_RawFastCallKeywords) no es necesario crear una tupla, solo indexar los argumentos .
Como dice su actualización, esto no se trata de int.__repr__ vs int.__str__ , son la misma función después de todo; se trata de cómo repr y str llegan a ellos. str solo necesita trabajar un poco más duro.
Hay varias posibilidades porque las funciones CPython que son responsables del str y repr return son ligeramente diferentes.
Pero supongo que la razón principal es que str es un type (una clase) y el método str.__new__ tiene que llamar a __str__ mientras que repr puede ir directamente a __repr__ .