web-dev-qa-db-fra.com

Comment utiliser python timeit lors du passage de variables aux fonctions?

Je suis aux prises avec cette utilisation de timeit et je me demandais si quelqu'un avait des conseils

Fondamentalement, j'ai une fonction (à laquelle je passe une valeur) que je veux tester la vitesse et créé ceci:

if __name__=='__main__':
    from timeit import Timer
    t = Timer(superMegaIntenseFunction(10))
    print t.timeit(number=1)

mais quand je le lance, j'obtiens des erreurs étranges comme venant du module timeit.

ValueError: stmt is neither a string nor callable

Si je lance la fonction seule, cela fonctionne bien. C'est quand je l'enveloppe dans le temps qu'il module, je reçois les erreurs (j'ai essayé d'utiliser des guillemets doubles et sans..samme sortie).

toute suggestion serait génial!

Merci!

70
Lostsoul

Faites-en un callable:

if __name__=='__main__':
    from timeit import Timer
    t = Timer(lambda: superMegaIntenseFunction(10))
    print(t.timeit(number=1))

Devrait marcher

115
Pablo

Timer(superMegaIntenseFunction(10)) signifie "appelle superMegaIntenseFunction(10), puis passe le résultat à Timer". Ce n'est clairement pas ce que vous voulez. Timer attend soit un appelable (comme son nom l'indique: quelque chose qui peut être appelé, tel qu'une fonction), ou une chaîne (afin qu'il puisse interpréter le contenu de la chaîne en tant que code Python). Timer fonctionne en appelant l'appelable-chose à plusieurs reprises et en voyant combien de temps est pris.

Timer(superMegaIntenseFunction) passera la vérification de type, car superMegaIntenseFunction est appelable. Cependant, Timer ne saurait pas quelles valeurs passer à superMegaIntenseFunction.

La solution simple consiste naturellement à utiliser une chaîne de caractères avec le code. Nous devons passer un argument 'setup' au code, car la chaîne est "interprétée en tant que code" dans un contexte récent - elle n'a pas accès à la même globals, vous devez donc exécuter un autre bit de code pour rendre le définition disponible - voir la réponse de @ oxtopus.

Avec lambda (comme dans la réponse de @ Pablo), nous pouvons lier le paramètre 10 à un appel à superMegaIntenseFunction. Nous ne faisons que créer une autre fonction, qui ne prend pas d'argument, et appelle superMegaIntenseFunction avec 10. C'est comme si vous aviez utilisé def pour créer une autre fonction de ce type, sauf que la nouvelle fonction n'a pas de nom (car elle n'en a pas besoin).

23
Karl Knechtel

Vous devriez passer une chaîne. c'est à dire.

t = Timer('superMegaIntenseFunction(10)','from __main__ import superMegaIntenseFunction')
18
Austin Marshall

Une note pour les futurs visiteurs. Si vous devez le faire fonctionner dans le débogueur pdb et que superMegaIntenseFunction ne soit pas dans la portée globale, vous pouvez le faire fonctionner en ajoutant à globals:

globals()['superMegaIntenseFunction'] = superMegaIntenseFunction
timeit.timeit(lambda: superMegaIntenseFunction(x))

Notez que la surcharge de temps est un peu plus grande dans ce cas en raison des appels de fonction supplémentaires. [la source]

1
Dennis Golomazov

Une façon de le faire serait d'utiliser partial pour que la fonction 'superMegaIntenseFunction' soit utilisée comme appelant (c'est-à-dire sans le ()) dans le minuteur ou directement dans timeit.timeit. L'utilisation de partial transmettra l'argument à la fonction lorsqu'elle sera appelée par le temporisateur.

from functools import partial
from timeit import timeit

print(timeit(partial(superMegaIntenseFunction, 10), number=1))
0
Coding Paradigm