Funktiondefs in Funktionen

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
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Beim folgenden Code verstehe ich verschiedene Dinge nicht:

Code: Alles auswählen

class KL(object):
  print "*** class KL"
  #
  def neu(self):
    print "*** def neu"
    #
    def foo():
      print "*** def foo", "kennt ii=%i"%ii
      return "foo "+str(ii)
    #
    for ii in range(9):
      print foo()


KL().neu()
Der Code druckt folgendes aus:

Code: Alles auswählen

*** class KL
*** def neu
*** def foo kennt ii=0
foo 0
*** def foo kennt ii=1
foo 1
*** def foo kennt ii=2
foo 2
*** def foo kennt ii=3
foo 3
*** def foo kennt ii=4
foo 4
*** def foo kennt ii=5
foo 5
*** def foo kennt ii=6
foo 6
*** def foo kennt ii=7
foo 7
*** def foo kennt ii=8
foo 8
und ich frage mich (und euch):
#
[ (1) Was geschieht mit dem Befehl 'print "*** class KL"' ] edit: falschmeldung
(2) Woher kennt die Definition von foo den Wert von ii?
(3) Warum wird die Definition von foo immer wieder neu ausgeführt?
#
Ich gehe davon aus, dass die obige Codierung schlecht ist, da Definitionen nur einmal ausgeführt werden sollten, und ich nicht 9 Objekte erstellen sollte, die alle gleich sind.
Zuletzt geändert von Goswin am Mittwoch 29. April 2009, 09:40, insgesamt 1-mal geändert.
Python nervt. (Nicht immer, und natürlich nur mich, niemals andere)
lunar

Goswin hat geschrieben:(1) Was geschieht mit dem Befehl 'print "*** class KL"'
Er wird ausgeführt ... warum er allerdings in deiner Ausgabe nicht auftaucht, kann ich nicht sagen.
(2) Woher kennt die Definition von foo den Wert von ii?
Lokale Funktionen haben Zugriff auf den sie umschließenden lokalen Namensraum. "ii" ist in diesem Namensraum definiert, folglich kann "foo()" darauf zugreifen. Das nennt sich "Closure".
(3) Warum wird die Definition von foo immer wieder neu ausgeführt?
Wie kommst du denn darauf, dass die Definition erneut ausgeführt wird? Ich sehe da nur einen neunmaligen Funktionsaufruf.
BlackJack

@Goswin:

(1) Das ``print`` wird ausgeführt während der Klassenkörper ausgeführt wird, und Du unterschlägst uns hier anscheinend diese Ausgabe.

(2) Wenn ein Name nicht im aktuellen Namensraum gefunden wird, wird in den lexikalisch umgebenden Namensräumen danach gesucht, also in diesem Fall in den lokalen Namen des `neu()`-Aufrufs.

(3) Weil das eben so definiert ist. ``def`` ist eine ausführbare Anweisung, die jedesmal ausgeführt wird, wenn der normale Programmfluss daran vorbei kommt.

Hier sind die Funktionsobjekte alle gleich, aber das muss ja nicht so sein. Da sie ihre "Umgebung" kennen, kann man durchaus Funktionsobjekte mit gleichem Code aber anderen gebundenen Werten erstellen. Stichwort "Closures". Beispiel:

Code: Alles auswählen

In [2]: def make_adder(value):
   ...:     def add_x(n):
   ...:         return n + value
   ...:     return add_x
   ...:

In [3]: add5 = make_adder(5)

In [4]: add5(10)
Out[4]: 15

In [5]: add5(42)
Out[5]: 47

In [6]: add23 = make_adder(23)

In [7]: add23(10)
Out[7]: 33

In [8]: add23(42)
Out[8]: 65

In [9]: add5(10)
Out[9]: 15
Das funktioniert nur wenn ``def`` wirklich jedesmal ein neues Funktionsobjekt erstellt.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Goswin hat geschrieben:(1) Was geschieht mit dem Befehl 'print "*** class KL"'
Bei mir wird der ausgegeben.
BlackJack hat geschrieben:(2) Wenn ein Name nicht im aktuellen Namensraum gefunden wird, wird in den lexikalisch umgebenden Namensräumen danach gesucht, also in diesem Fall in den lokalen Namen des `neu()`-Aufrufs.
Bei lesendem Zugriff. Bei schreibendem Zugriff gibt's eine Fehlermeldung.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Moin,
Rebecca hat geschrieben:Bei lesendem Zugriff. Bei schreibendem Zugriff gibt's eine Fehlermeldung.
warum sollte das passieren?

Gruß,
Manuel
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Code: Alles auswählen

a = 1

def f():
    print a

def g():
    a += 1  # Oh, oh...
    print a

f()
g()

Code: Alles auswählen

1
Traceback (most recent call last):
  File "test.py", line 11, in <module>
    g()
  File "test.py", line 7, in g
    a += 1
UnboundLocalError: local variable 'a' referenced before assignment
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Goswin, wäre es nicht einfacher, das in der Sprachspezifikation definierte Verhalten einfach nachzulesen, als es mühsam "reverse zu engineeren"? Wenn Python auf eine Klassendefinition trifft, wird der Rumpf in einer neuen locals-Umgebung ausgeführt, die dann das __dict__ des danach erzeugten Klassenobjekts wird. Eine for-Schleife, "leaked" ihre Variable, d.h. "ii" ist auch noch nach der Schleife definiert. Dazu kommt das Verhalten, dass es egal ist, wo innerhalb einer Funktion eine Variable definiert ist, sie wird überall als lokal angesehen. Daher ist "ii" auch von lexikografisch vor der Schleife bereits definiert. Und Funktionsdefinition (oder Klassendefinition) sind auch nur Anweisungen und werden natürlich jedes Mal, wenn der Kontrollfluss sie erreicht, erneut ausgeführt.

Stefan
Benutzeravatar
helduel
User
Beiträge: 300
Registriert: Montag 23. Juli 2007, 14:05
Wohnort: Laupheim

Ah, ok. Wobei folgendes geht:

Code: Alles auswählen

def foo():
    def bar():
        x['a'] = 5
    x = {'a': 9}
    bar()
    print x

foo()
# {'a': 5}
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

Meine Frage (1) ist unberechtigt; ich hatte in der Tat die Ausgabe von <print "*** class KL"> übersehen und bitte alle um Verzeihung, die mich darauf hingewiesen haben.

Die Antworten zu den beiden anderen Fragen muss ich erst einmal verdauen :?, vielen Dank für eure Bemerkungen!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

helduel hat geschrieben:Wobei folgendes geht:

Code: Alles auswählen

def foo():
    def bar():
        x['a'] = 5
    x = {'a': 9}
    bar()
    print x

foo()
# {'a': 5}
Natürlich, weil es den Scope nicht schreibt sondern nur liest. Was du mit (mutablen) Objekten machst ist an dieser Stelle egal.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

OK, ich glaube, ich habe es verstanden:
#
Der Befehl in einem <class KL(object): befehl> wird während des Aufbaus der Klasse (in einem provisorischen Kontext) richtig ausgeführt.
Der Befehl in einem <def foo(arg): befehl> wird während des Aufbaus der Funktion nur niedergeschrieben, um später bei seinem Aufruf ausgeführt zu werden.

Code: Alles auswählen

def foo(x,y):
  return bild(x+y)
bild = lambda u: '<{[ '+str(u)+' ]}>'
#
print foo(2,3)  #druckt "<{[ 5 ]}>"
Python nervt. (Nicht immer, und natürlich nur mich, niemals andere)
Antworten