Seite 1 von 1

Funktiondefs in Funktionen

Verfasst: Mittwoch 29. April 2009, 08:04
von Goswin
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.

Re: Funktiondefs in Funktionen

Verfasst: Mittwoch 29. April 2009, 08:19
von 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.

Verfasst: Mittwoch 29. April 2009, 08:25
von 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.

Re: Funktiondefs in Funktionen

Verfasst: Mittwoch 29. April 2009, 08:34
von Rebecca
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.

Re: Funktiondefs in Funktionen

Verfasst: Mittwoch 29. April 2009, 08:56
von helduel
Moin,
Rebecca hat geschrieben:Bei lesendem Zugriff. Bei schreibendem Zugriff gibt's eine Fehlermeldung.
warum sollte das passieren?

Gruß,
Manuel

Verfasst: Mittwoch 29. April 2009, 09:13
von Rebecca

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

Verfasst: Mittwoch 29. April 2009, 09:40
von sma
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

Verfasst: Mittwoch 29. April 2009, 09:41
von helduel
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}

Verfasst: Mittwoch 29. April 2009, 09:57
von Goswin
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!

Verfasst: Mittwoch 29. April 2009, 12:53
von Leonidas
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.

Verfasst: Mittwoch 29. April 2009, 14:13
von Goswin
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 ]}>"