Wie verwende ich Python Timeit, wenn Variablen an Funktionen übergeben werden?

76

Ich kämpfe mit Timeit damit und habe mich gefragt, ob jemand irgendwelche Tipps hat

Grundsätzlich habe ich eine Funktion (an die ich einen Wert übergebe), deren Geschwindigkeit ich testen und erstellen möchte:

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

aber wenn ich es starte, bekomme ich seltsame Fehler wie vom timeit-Modul:

ValueError: stmt is neither a string nor callable

Wenn ich die Funktion alleine ausführe, funktioniert sie einwandfrei. Wenn ich es in das Time-It-Modul einbinde, erhalte ich die Fehler (ich habe versucht, doppelte Anführungszeichen und ohne ... gleiche Ausgabe zu verwenden).

Vorschläge wären super!

Vielen Dank!

Verlorene Seele
quelle

Antworten:

126

Machen Sie es aufrufbar:

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

Sollte arbeiten

Pablo
quelle
Das hat funktioniert! Vielen Dank. Ich muss herausfinden, was Lambda tut. Scheint so, als hätte das den Unterschied gemacht. Danke Pablo
Lostsoul
6
Wenn dies nur irgendwo in der Dokumentation wäre
Endolith
17
Oh, aber Lambda fügt etwas Overhead hinzu, also nicht ideal zum Testen kleiner Dinge. timeit 5*5beträgt 33 ns und timeit (lambda: 5*5)()233 ns.
Endolith
Dies ist perfekt. Ich hatte ein Problem beim Aufrufen timeitmit dem Lambda (das Skript ist eingefroren; die Methode, mit der ich die Ausführungszeit gemessen habe, ist das retrbinaryHerunterladen über FTP mit dem ftplib). Sobald ich das TimerObjekt benutzte , wirkte es plötzlich wie ein Zauber. Keine Ahnung, was passiert ist ...: D
rbaleksandar
23

Timer(superMegaIntenseFunction(10))bedeutet "anrufen superMegaIntenseFunction(10), dann das Ergebnis an übergeben Timer". Das ist eindeutig nicht das, was du willst. Timererwartet entweder einen aufrufbaren (so wie es sich anhört: etwas, das aufgerufen werden kann, wie eine Funktion) oder einen String (damit der Inhalt des Strings als Python-Code interpretiert werden kann). Timerfunktioniert, indem Sie das aufrufbare Ding wiederholt aufrufen und sehen, wie viel Zeit benötigt wird.

Timer(superMegaIntenseFunction)würde die Typprüfung bestehen, da superMegaIntenseFunctionaufrufbar ist. Ich Timerwürde jedoch nicht wissen, an welche Werte übergeben werden soll superMegaIntenseFunction.

Der einfache Weg, dies zu umgehen, besteht natürlich darin, eine Zeichenfolge mit dem Code zu verwenden. Wir müssen ein 'Setup'-Argument an den Code übergeben, da die Zeichenfolge in einem neuen Kontext "als Code interpretiert" wird - sie hat keinen Zugriff darauf globals, sodass Sie ein weiteres Stück Code ausführen müssen, um die Definition zu erstellen verfügbar - siehe Antwort von @ oxtopus.

Mit lambda(wie in der Antwort von @ Pablo) können wir den Parameter 10an einen Aufruf von binden superMegaIntenseFunction. Alles , was wir tun , eine andere Funktion schafft, die keine Argumente übernimmt, und fordert superMegaIntenseFunctionmit 10. Es ist so, als hätten Sie defeine andere Funktion wie diese erstellt, nur dass die neue Funktion keinen Namen erhält (weil sie keinen benötigt).

Karl Knechtel
quelle
18

Sie sollten eine Zeichenfolge übergeben. dh

t = Timer('superMegaIntenseFunction(10)','from __main__ import superMegaIntenseFunction')
Austin Marshall
quelle
Danke für die Antwort oxtopus! Es funktioniert nicht, wenn ich es in Anführungszeichen setze, daher ist es eine Zeichenfolge, bei der folgende Fehlermeldung angezeigt wird: NameError: Der globale Name 'superMegaIntenseFunction' ist nicht definiert. Was kann ich sonst noch versuchen?
Lostsoul
Die Antwort wurde korrigiert, um das Setup-Argument einzuschließen. ( docs.python.org/library/timeit.html#timeit.Timer )
Austin Marshall
1

Ein Hinweis für zukünftige Besucher. Wenn Sie dafür sorgen müssen, dass es im pdbDebugger funktioniert und superMegaIntenseFunctionnicht im globalen Bereich liegt, können Sie es zum Laufen bringen, indem Sie Folgendes hinzufügen globals:

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

Beachten Sie, dass der Timing-Overhead in diesem Fall aufgrund der zusätzlichen Funktionsaufrufe etwas größer ist. [Quelle]

Dennis Golomazov
quelle
0

Eine Möglichkeit wäre die Verwendung von Partial, sodass die Funktion 'superMegaIntenseFunction' als aufrufbare Funktion (dh ohne ()) im Timer oder direkt in timeit.timeit verwendet wird. Bei Verwendung von Partial wird das Argument an die Funktion übergeben, wenn es vom Timer aufgerufen wird.

from functools import partial
from timeit import timeit

print(timeit(partial(superMegaIntenseFunction, 10), number=1))
Codierungsparadigma
quelle