Je me demande pourquoi repr(int)
est plus rapide que str(int)
. Avec l'extrait de code suivant:
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()
J'obtiens ces timings (python 3.5.2, mais des résultats très similaires avec 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 je suis sur la bonne voie, la même fonction long_to_decimal_string
Est appelée sous le capot.
Ai-je eu quelque chose de mal ou quoi d'autre qui me manque?
update : Cela n'a probablement rien à voir avec les méthodes __repr__
ou __str__
de int
mais avec les différences entre repr()
et str()
, car int.__str__
et int.__repr__
sont en fait relativement rapides:
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()
résulte 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)
Parce que l'utilisation de str(obj)
doit d'abord passer par type.__call__
Puis str.__new__
(Créer une nouvelle chaîne) puis PyObject_Str
(faire une chaîne avec l'objet) qui invoque int.__str__
et, enfin , utilise la fonction que vous avez liée.
repr(obj)
, qui correspond à builtin_repr
, appelle directement PyObject_Repr
(récupère l'objet repr) qui appelle alors int.__repr__
qui utilise la même fonction que int.__str__
.
De plus, le chemin qu'ils empruntent call_function
(la fonction qui gère CALL_FUNCTION
Opcode généré pour les appels) est légèrement différent.
Depuis la branche principale sur GitHub (CPython 3.7):
str
passe par _PyObject_FastCallKeywords
(qui est celui qui appelle type.__call__
). En plus d'effectuer plus de vérifications, cela doit également créer un Tuple pour contenir les arguments positionnels (voir _PyStack_AsTuple
).repr
passe par _PyCFunction_FastCallKeywords
qui appelle _PyMethodDef_RawFastCallKeywords
. repr
a également de la chance car, puisqu'il n'accepte qu'un seul argument (le commutateur le mène au cas METH_0
dans _PyMethodDef_RawFastCallKeywords
), il n'est pas nécessaire de créer un Tuple, juste - indexation des arguments .Comme l'indique votre mise à jour, il ne s'agit pas de int.__repr__
Vs int.__str__
, Ils sont la même fonction après tout; il s'agit de savoir comment repr
et str
les atteindre. str
a juste besoin de travailler un peu plus fort.
Je viens de comparer les implémentations str
et repr
dans la branche 3.5. Voir ici .
Il existe plusieurs possibilités car les fonctions CPython responsables des retours str
et repr
sont légèrement différentes.
Mais je suppose que la raison principale est que str
est un type
(une classe) et le str.__new__
la méthode doit appeler __str__
tandis que repr
peut aller directement à __repr__
.