J'ai joué avec timeit
en Python, j'ai eu un problème étrange.
Je définis une fonction simple add
. timeit
fonctionne lorsque je passe add
deux paramètres de chaîne. Mais cela déclenche ValueError: stmt n'est ni une chaîne ni appelable
quand je passe add
deux paramètres int
.
>>> import timeit >>> def add(x,y): ... return x + y ... >>> a = '1' >>> b = '2' >>> timeit.timeit(add(a,b)) 0.01355926995165646 >>> a = 1 >>> b = 2 >>> timeit.timeit(add(a,b)) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/anaconda/lib/python3.6/timeit.py", line 233, in timeit return Timer(stmt, setup, timer, globals).timeit(number) File "/anaconda/lib/python3.6/timeit.py", line 130, in __init__ raise ValueError("stmt is neither a string nor callable") ValueError: stmt is neither a string nor callable
Pourquoi le type de paramètre est-il important ici?
4 Réponses :
Ma question est de savoir pourquoi le type de paramètre est important ici?
Les arguments de fonction sont entièrement évalués avant l'appel de la fonction. Cela signifie que lorsque vous faites:
timeit.timeit(add(a,b))Ensuite,
add (a, b)
a déjà été calculé avant quetimeit
ne soit utilisé . Donc, ça n'a rien à voir avec le temps.La raison pour laquelle
timeit.timeit (add (a, b))
"fonctionne" quand a et b sont des chaînes numériques est juste ridicule : il chronomètre l'évaluation de'12'
. Le résultat de l'appel deadd ('1', '2')
se trouve être une chaîne valide de code Python ici.timeit
le compile et suppose que vous vouliez chronométrer l'évaluation de l'entier littéral 12.
merci pour votre réponse généreuse! Cela signifie-t-il que passer add («1», «2») équivaut au code suivant? timeit.timeit ("add (1,2)", "from main import add")
Comment l'OP doit-il modifier le code afin que le temps d'évaluation de la somme des entiers puisse être mesuré?
@liangli Pas exactement. Mais c'est une façon correcte de chronométrer l'exécution réelle de add
en utilisant des arguments entiers.
@liangli: non, c'est la valeur de retour de add ()
qui est passée à timeit
. add ('1', '2')
renvoie la chaîne '12'
, c'est donc l'équivalent de timeit ('12 ')
.
puisque add (a, b) passé n'est pas une chaîne, je suis curieux de savoir ce qui se passe sous le capot. disons, pourquoi timeit ne prend-il pas add (1,2) comme chaîne valide de code python?
@liangli: add (1, 2)
n'est pas une chaîne , c'est une expression qui renvoie un résultat.
Votre erreur est de supposer que Python transmet l'expression Donc pour C'est tout parfaitement normal; sinon, comment pourriez-vous passer directement le résultat d'une fonction à une autre fonction? Ce que vous voulez, c'est pour passer une chaîne avec l'expression à Vous obtenez maintenant des résultats plus significatifs: L'ajout d'entiers est donc plus rapide que l'ajout de chaînes. Vous voudrez probablement essayer ceci avec différentes tailles d'entiers et de chaînes, mais l'ajout d'entiers restera le résultat le plus rapide. add (a, b)
à timeit ()
. Ce n'est pas le cas, add (a, b)
n'est pas une chaîne, c'est une expression donc Python à la place exécute add (a, b) code > et le résultat de cet appel est transmis à l'appel
timeit ()
. add ('1', '2') code> le résultat est
'12'
, une chaîne. Passer une chaîne à timeit ()
est très bien. Mais add (1, 2)
est 12
, un entier. timeit (12)
vous donne une exception. Non pas que le timing '12'
soit tout ce qui est intéressant, bien sûr, mais c'est une expression Python valide produisant la valeur entière 12: >>> import timeit
>>> def add(x, y):
... return x + y
...
>>> a = '1'
>>> b = '2'
>>> timeit.timeit('add(a, b)', 'from __main__ import add, a, b')
0.16069997000158764
>>> a = 1
>>> b = 2
>>> timeit.timeit('add(a, b)', 'from __main__ import add, a, b')
0.10841095799696632
timeit.timeit ()
est juste une autre fonction Python , rien de si spécial que cela désactivera l'évaluation normale des expressions. timeit ()
. timeit ()
n'a pas accès à votre fonction add ()
, ni à a
ou b
, vous besoin de lui donner accès avec le deuxième argument, la chaîne de configuration. Vous pouvez utiliser de __main__ import add, a, b
pour importer l'objet de fonction add
: timeit.timeit('add(a, b)', 'from __main__ import add, a, b')
>>> import timeit
>>> def add(x, y):
... return x + y
...
>>> a = '1'
>>> b = '2'
>>> add(a, b)
'12'
>>> timeit.timeit('12')
0.009553937998134643
>>> a = 1
>>> b = 2
>>> add(a, b)
3
>>> timeit.timeit(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/.../lib/python3.7/timeit.py", line 232, in timeit
return Timer(stmt, setup, timer, globals).timeit(number)
File "/.../lib/python3.7/timeit.py", line 128, in __init__
raise ValueError("stmt is neither a string nor callable")
ValueError: stmt is neither a string nor callable
une autre façon de résoudre le type "problème" est de passer le résultat de la fonction sous forme de chaîne!
timeit.timeit('%s'%(add(1,2))) or timeit.timeit(f'{add(1,2)}')
Avec la version chaîne, add renvoie une chaîne, que timeit peut évaluer. Donc "12" est une expression python valide alors que 3 ne l'est pas.
timeit.timeit(lambda: add(1,2))
La meilleure façon d'utiliser timeit, est de déformer la fonction que vous voulez tester avec un lambda:
XXX
C'est beaucoup plus élégant que de jouer avec des chaînes. Je ne comprends vraiment pas pourquoi tous les exemples utilisent des chaînes.
Pourquoi le type de l'objet n'a-t-il pas d'importance?