timeit für inner function einer Methode

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
lunas
User
Beiträge: 87
Registriert: Samstag 2. Dezember 2006, 10:56

Hi,

ich habe mal wieder ein kurioses Problem. Und zwar habe ich eine Methode einer Klasse, die hauptsächlich aus 2 Teilen besteht. Um zu überprüfen welcher dieser Teile am meisten Zeit benötigt und dann aubzuschätzen an welcher Stelle Optimierungsbedarf besteht, habe ich die 2 Teile in 2 innere Funktionen (richtiger Terminus?) gepackt. Nun möchte ich sie gerne via timeit aufrufen und die Ausführungszeit ausgeben.

Code: Alles auswählen

import timeit
class Foo:
	def bar(self):
		def bar1():
			print '1'
		def bar2():
			print '2'
		#bar1()
		#bar2()
		t = timeit.Timer("bar1()", "from __main__ import Foo")
		print t.timeit()
		t = timeit.Timer("bar2()", "from __main__ import Foo")
		print t.timeit()

Foo().bar()
Das Problem ist folgendes:

Code: Alles auswählen

Traceback (most recent call last):
  File "D:\snippet.py", line 15, in <module>
    Foo().bar()
  File "D:\snippet.py", line 11, in bar
    print t.timeit()
  File "C:\Programme\Python25\lib\timeit.py", line 161, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
NameError: global name 'bar1' is not defined
Was mache ich falsch? Ich habe es auch schon ohne den 2. Parameter im Timer-Konstruktor versucht - gleiches Ergebnis...
Es scheint als würde timeit die Funktionen bar1 und bar2 in einem völlig anderen Scope aufrufen und deshalb nicht auf die Funktionen zugreifen können...

Aber so recht bringt mich das auch nicht weiter :?

Vielleicht kann jemand helfen.

lunas
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

ehm... ist jetzt nur spontan und ungetestet:

versuchs mal mit

Code: Alles auswählen

t = timeit.Timer("self.bar1()")
denn eigentlich uebergibst du timeit doch einfach nur die auszufuehrende methode... ach mist... natuerlich haengen deine beiden methoden auch noch innerhalb einer anderen. Na dann wuerde ich vorschlagen einfach die Zeile:

Code: Alles auswählen

global bar1, bar2
an den Anfang von bar() zu setzen :roll:
[url=http://www.python-forum.de/post-86552.html]~ Wahnsinn ist auch nur eine andere Form der Intelligenz ~[/url]
hackerkey://v4sw6CYUShw5pr7Uck3ma3/4u7LNw2/3TXGm5l6+GSOarch/i2e6+t2b9GOen7g5RAPa2XsMr2
lunas
User
Beiträge: 87
Registriert: Samstag 2. Dezember 2006, 10:56

Code: Alles auswählen

t = timeit.Timer("self.bar1()")
führt zu:

Code: Alles auswählen

  File "<timeit-src>", line 6, in inner
NameError: global name 'self' is not defined
Na dann wuerde ich vorschlagen einfach die Zeile:

Code: Alles auswählen

global bar1, bar2
an den Anfang von bar() zu setzen :roll:
Gleiches Ergebnis wie vorher:

Code: Alles auswählen

  File "<timeit-src>", line 6, in inner
NameError: global name 'bar1' is not defined
Trotzdem Danke.
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

lunas hat geschrieben: Gleiches Ergebnis wie vorher:

Code: Alles auswählen

  File "<timeit-src>", line 6, in inner
NameError: global name 'bar1' is not defined
Trotzdem Danke.
Ja keine Umstaende... aber da muss es schon irgendwie ne Loesung geben. Dummerweise hab ich timeit nie vorher verwendet und dachte es arbeitet mit exec, bzw eval. Vielleicht tuts das ja auch, aber dann weiß ich noch nicht mit welchem Scope.

Naja, ich find das schon raus... ^^
[url=http://www.python-forum.de/post-86552.html]~ Wahnsinn ist auch nur eine andere Form der Intelligenz ~[/url]
hackerkey://v4sw6CYUShw5pr7Uck3ma3/4u7LNw2/3TXGm5l6+GSOarch/i2e6+t2b9GOen7g5RAPa2XsMr2
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Wie wäre es damit: du bindest bar1 und bar2 an die Foo klasse, per self.bar1 = bar1 und rufst dann in timeit Foo.bar1 auf?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

hmm... ist aber generell seltsam, da nichtmal sowas in der Art funktioniert:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

from timeit import Timer

class FOO:
        def bar(self):
                global bar1, bar2
                def bar1():
                        print "ich bin bar1"
                
                def bar2():
                        print "ich bin bar2"

foo = FOO()
foo.bar()

# die folgenden Aufrufe funktionieren...
bar1()
bar2()

# hier werden aber bar1 und bar2 nicht in global() gefunden o,O
t = Timer("bar1()")
print t.timeit()
t = Timer("bar2()")
print t.timeit()
[url=http://www.python-forum.de/post-86552.html]~ Wahnsinn ist auch nur eine andere Form der Intelligenz ~[/url]
hackerkey://v4sw6CYUShw5pr7Uck3ma3/4u7LNw2/3TXGm5l6+GSOarch/i2e6+t2b9GOen7g5RAPa2XsMr2
BlackJack

``def`` ist keine Deklaration sondern eine ausführbare Anweisung, das heisst so ein ``def`` muss auch ausgeführt werden um die Bindung zwischen dem Namen und dem Funktionscode herzustellen. Solange die `Foo.bar()`-Methode nicht ausgeführt wird, werden auch die beiden enthaltenen ``def``\s nicht ausgeführt und somit `bar1` und `bar2` nicht an Namen gebunden.

Wenn die beiden nicht als ``global`` deklariert sind, dann gibt es die Funktionen nur lokal während der Ausführung. Also weder vor dem Aufruf noch nach dem Aufruf. Es sei denn man benutzt eine lokal definierte Funktion als Rückgabewert, dann existiert sie natürlich weiterhin, solange das Funktionsobjekt von irgendwo referenziert wird.
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

BlackJack hat geschrieben:``def`` ist keine Deklaration sondern eine ausführbare Anweisung, das heisst so ein ``def`` muss auch ausgeführt werden um die Bindung zwischen dem Namen und dem Funktionscode herzustellen. Solange die `Foo.bar()`-Methode nicht ausgeführt wird, werden auch die beiden enthaltenen ``def``\s nicht ausgeführt und somit `bar1` und `bar2` nicht an Namen gebunden.

Wenn die beiden nicht als ``global`` deklariert sind, dann gibt es die Funktionen nur lokal während der Ausführung. Also weder vor dem Aufruf noch nach dem Aufruf. Es sei denn man benutzt eine lokal definierte Funktion als Rückgabewert, dann existiert sie natürlich weiterhin, solange das Funktionsobjekt von irgendwo referenziert wird.
Ja nun schau dir mal mein Code-Stueck an... ich tue eigentlich genau das erforderliche... oder sitzt mir noch zuviel Schlaf in den Augen? :oops:
[url=http://www.python-forum.de/post-86552.html]~ Wahnsinn ist auch nur eine andere Form der Intelligenz ~[/url]
hackerkey://v4sw6CYUShw5pr7Uck3ma3/4u7LNw2/3TXGm5l6+GSOarch/i2e6+t2b9GOen7g5RAPa2XsMr2
BlackJack

Ups. In der Tat, im Quelltext selber passiert alles notwendige. Nur der "Timeit"-Code wird in einem eigenen Namensraum ausgeführt. Da musst Du die Sachen noch importieren:

Code: Alles auswählen

t = Timer("bar1()", "from __main__ import *")
Benutzeravatar
nkoehring
User
Beiträge: 543
Registriert: Mittwoch 7. Februar 2007, 17:37
Wohnort: naehe Halle/Saale
Kontaktdaten:

Aha... na damit haben wir doch eine passable Loesung fuer Lunas, oder nicht?
[url=http://www.python-forum.de/post-86552.html]~ Wahnsinn ist auch nur eine andere Form der Intelligenz ~[/url]
hackerkey://v4sw6CYUShw5pr7Uck3ma3/4u7LNw2/3TXGm5l6+GSOarch/i2e6+t2b9GOen7g5RAPa2XsMr2
lunas
User
Beiträge: 87
Registriert: Samstag 2. Dezember 2006, 10:56

Ja, so läuft es. Zwar bekomme ich noch eine Warnung, aber damit kann ich leben.

Code: Alles auswählen

<timeit-src>:2: SyntaxWarning: import * only allowed at module level
1
0.000717409614909
<timeit-src>:2: SyntaxWarning: import * only allowed at module level
2
0.000674387387224
Hier ist noch einmal die korrigierte Version des Codes:

Code: Alles auswählen

import timeit
global bar1, bar2
class Foo:
	def bar(self):
		global bar1, bar2
		def bar1():
			print '1'
		def bar2():
			print '2'
		t = timeit.Timer("bar1()", "from __main__ import *")
		print t.timeit(number=1)
		t = timeit.Timer("bar2()", "from __main__ import *")
		print t.timeit(number=1)

Foo().bar()
Vielen Dank für eure Hilfe.

lunas
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

lunas hat geschrieben:Ja, so läuft es. Zwar bekomme ich noch eine Warnung, aber damit kann ich leben.

Code: Alles auswählen

<timeit-src>:2: SyntaxWarning: import * only allowed at module level
1
0.000717409614909
<timeit-src>:2: SyntaxWarning: import * only allowed at module level
2
0.000674387387224
Statt des Sterns kannst du ja bar1 und bar2 schrieben, dann sollte auch die verschwinden.

SyntaxWarnings wegen Stern-Importen.. eigentlich eine gute Idee :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Leonidas hat geschrieben:[...]
SyntaxWarnings wegen Stern-Importen.. eigentlich eine gute Idee :)
Finde ich auch. :) Sollte man generell einführen :D
Ene Uran
User
Beiträge: 125
Registriert: Sonntag 17. September 2006, 20:14
Wohnort: Hollywood

Das global kann man wirklich weglassen:

Code: Alles auswählen

import timeit

class Foo:
    def bar(self):
        def bar1():
            print '1'
        def bar2():
            print '2'
        t = timeit.Timer("bar1()", "from __main__ import bar1")
        print t.timeit(number=1)
        t = timeit.Timer("bar2()", "from __main__ import bar2")
        print t.timeit(number=1)

Foo().bar()
Edit (Leonidas): Code-Highlighting eingeschaltet.
Atomkraftwerkaktienbesitzer
lunas
User
Beiträge: 87
Registriert: Samstag 2. Dezember 2006, 10:56

Ene Uran hat geschrieben:Das global kann man wirklich weglassen:
Das erste 'global bar1, bar2' ist wohl noch vom Herumprobieren drin gewesen und kann natürlich weggelassen werden. Aber das zweite global, nach der Definition von bar, ist schon vonnöten, andernfalls bekomme ich diese Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "snippet.py", line 38, in <module>
    Foo().bar()
  File "snippet.py", line 35, in bar
    print t.timeit(number=1)
  File "timeit.py", line 161, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 3, in inner
ImportError: cannot import name bar1
lunas
Antworten