Seite 1 von 1

Hier die Konstanten in Python... :-)

Verfasst: Freitag 11. Dezember 2009, 18:26
von alpha
Aber im Ernst.. ich bin auf ein Phänomen gestoßen, für das ich keine Erklärung habe. Vielleicht kann mir jemand das erläutern.

Folgendes Programm:

Code: Alles auswählen

var = 42

def foo():
    print var
    
foo()
führt zu folgender Ausgabe:
42
Ich weiß.. was ihr denkt... Der Typ ist seit 7 Jahren im Forum und das ist alles was er hinbekommen hat??? :roll:

Aber jetzt was mich wirklich verwundert:
Folgendes:

Code: Alles auswählen

var = 42

def foo():
    print var
    var = 43
    
foo()
führt zu:
Traceback (most recent call last):
File "seltsam.py", line 7, in <module>
foo()
File "seltsam.py", line 4, in foo
print var
UnboundLocalError: local variable 'var' referenced before assignment
alpha@dualcore:~/EigeneDateien/python$
Der Interpreter meckert nicht die Zuweisung an... (wobei ich auch das nicht verstehen würde, da ja vorher var auch in der Funktion bekannt war) der Interpreter frisst den "print" nicht, welcher zuvor ja noch funktioniert hat...
Es handelt sich übrigens um Python 2.6

Wie erklärt Ihr euch das? (bzw mir) :)
Grüße und ein schönes WE
alpha

Verfasst: Freitag 11. Dezember 2009, 18:41
von Hyperion
Naja, im ersten Fall wird der Name var innerhalb der Funktion foo() ja nicht neu gebunden, im zweiten Fall eben schon. Der Interpreter wird so "schlau" sein, dieses vor dem Ausführen zu prüfen. Deshalb meckert er eben und führt nicht zuerst print mit dem Wert des Bindings auf Modulebene aus.

Das von Dir erwartete Verhalten würde ja zu extrem verwirrenden und kaum mehr zu durchschauenden Seiteneffekten führen.

Code: Alles auswählen

var = 42

def foo(bar):
    if bar:
        var = 43
    print var

foo(True)
foo(False)
Hier wäre ja nicht mehr klar, an welchen Wert var gebunden ist, bzw. dies wäre vom Parameter abhängig.

Ich denke die Profis hier werden das noch besser darstellen können :-)

Verfasst: Freitag 11. Dezember 2009, 18:41
von ms4py
Der Trace sagt eigentlich schon alles ;)
"var" kommt innerhalb des Blocks als lokale Variable vor, und wird deshalb als lokale Variable gehandelt. Bei dem print ist "var" als lokale Variable noch nicht definiert, deshalb der Fehler.
Wie genau der Interpreter das erkennt und warum das genau so gehandhabt hat, kann ich allerdings nicht sagen. Eventuell kennt sich da jemand mehr aus.
Ich persönlich empfinde dieses Verhalten allerdings als logisch und nachvollziehbar.

Verfasst: Freitag 11. Dezember 2009, 19:11
von alpha
Danke erstmal für die Antworten.
Was ich aber nicht verstehe ist die Gültigkeit de Variable "var".
Einerseits ist sie Global gültig (ich kann sie aus jeder Funktion lesen) aber ich kann sie nicht überschreiben. Das ist etwas, was mir noch nicht so in den Kopf will...

Verfasst: Freitag 11. Dezember 2009, 19:19
von jbs
Du kannst lesend auf sie zugreifen. Willst du sie verändern, musst du `global` benutzen.

Das ist aber dreckig und sollte vermieden werden.

Verfasst: Freitag 11. Dezember 2009, 19:20
von Hyperion
alpha hat geschrieben:Danke erstmal für die Antworten.
Was ich aber nicht verstehe ist die Gültigkeit de Variable "var".
Einerseits ist sie Global gültig (ich kann sie aus jeder Funktion lesen) aber ich kann sie nicht überschreiben. Das ist etwas, was mir noch nicht so in den Kopf will...
Deswegen ist sie ja auch nicht "global". Ein Name ist prinzipiell immer innerhalb seines Blocks gültig. Wenn Du einen Wert auf Modul-Ebene an einen Namen bindest (hier eben var = 42), dann kann man aus tieferen Ebenen lesend darauf zugreifen.

Ein "Schreibzugriff", wie Du es nennst, ist nichts anderes als eine neue Bindung eines Namens. Passiert das nun innerhalb des selben Blocks, so bindest Du einen neuen Wert an den bereits vorher verwendeten Namen. Passiert das in einem anderen Block, so bindest Du in diesem Block einen Wert an einen dort neuen Namen.

Ob man lesend auf Namen anderer Blöcke zugreifen können muss, kann ich nicht beantworten (evtl. funzen sonst manche Pattern nicht). Aber Guido wird sich etwas dabei gedacht haben.

Dass das Binden genau so funktioniert ist imho sehr sinnvoll. Ansonsten dürfte es ja niemals 2x den gleichen Namen innerhalb von Python geben...

Verfasst: Freitag 11. Dezember 2009, 19:23
von Defnull
Du kannst sie überschreiben, aber das musst du tun, bevor du darauf zu greifst. Erst auf eine globale Variable zu greifen und dann eine Lokale mit dem selben Namen erstellen ist nicht möglich. Wäre es möglich, wäre folgender Code ziemlich verwirrend:

Code: Alles auswählen

 var = 'global'
while True:
    print var
    var = 'local'
Exakt die selbe Print-Anweisng würde einmal auf die globale und einmal auf die lokale Variable zu greifen. Bei etwas kompliziertenŕem Code könnte niemand mehr den Überblick behalten, was nun global und was lokal ist. Außerdem müsste der Interpreter jedes mal nach der Variable 'suchen', wenn er nicht vorher weiß, ob sie lokal oder global ist.

Re: Hier die Konstanten in Python... :-)

Verfasst: Freitag 11. Dezember 2009, 19:25
von crs
solange 'var' nur gelesen wird, aendert sich der scope nicht. d.h. der interpreter findet dann (nur) das globale binding. durch die zuweisung in der funktion wird der scope fuer 'var' dann allerdings auf local gesetzt, wodurch vor der zuweisung der entsprechende fehler auftritt.

mit global kann man den scope aendern:

Code: Alles auswählen

var = 42

def foo():
    global var
    print var
    var = 43
edit: python3 kennt auch noch nonlocal, womit man den scope auf die naechsthoehere ebene verlegen kann. trotzdem ist aber nur ein scope fuer jede variable pro funktion moeglich.

Code: Alles auswählen

def acc():
    n = 0
    def f():
        nonlocal n
        n += 1
        return n
    return f

Verfasst: Freitag 11. Dezember 2009, 19:49
von alpha
Erfüllt jedenfalls so doch recht gut den Zweck einer Konstanten IMHO

Danke für die Erklärungen...
Warum er jetzt aber die Zeile mit dem "print" anmeckert und nicht die Zeile mit der Zuweisung ist trotzdem komisch, oder nicht?

Verfasst: Freitag 11. Dezember 2009, 19:58
von jbs
Naja, warum steht oben.
Eine Konstante ist aber etwas anderes.

Verfasst: Freitag 11. Dezember 2009, 23:26
von ms4py
jbs hat geschrieben:Eine Konstante ist aber etwas anderes.
Wie definierst du dann eine Konstante, anstatt auf Modul-Ebene?

Verfasst: Freitag 11. Dezember 2009, 23:50
von Defnull
Konstanten sind konstant, also nicht veränderbar.

Code: Alles auswählen

a = []
def func():
  a.append(1)
a kann manipuliert werden -> a ist keine Konstante.

Verfasst: Samstag 12. Dezember 2009, 00:02
von ms4py
Defnull hat geschrieben:a kann manipuliert werden -> a ist keine Konstante.
Wie macht man es denn dann?

Verfasst: Samstag 12. Dezember 2009, 00:04
von cofi
In Python? Gar nicht. Allenfalls durch Konvention.

Verfasst: Samstag 12. Dezember 2009, 09:15
von BlackJack
"Konstanten" funktionieren in Python ähnlich wie ``private`` -- wer einen Namen, der komplett in Grossbuchstaben geschrieben ist, an einen anderen Wert bindet, sollte wissen was er da tut. :-)

Verfasst: Samstag 12. Dezember 2009, 10:28
von ms4py
BlackJack hat geschrieben:"Konstanten" funktionieren in Python ähnlich wie ``private`` -- wer einen Namen, der komplett in Grossbuchstaben geschrieben ist, an einen anderen Wert bindet, sollte wissen was er da tut. :-)
Eben! Deshalb finde ich dieses Verhalten vom Interpreter so auch nachvollziehbar. Wenn man auf Variablen von "höheren" Blöcke nicht lesend zugreifen könnte, kann man ja überhaupt gar keine "Konstanten" verwenden...
(Ich persönlich finde auch, dass man diese Konvention ruhig als "Konstante in Python" betrachten kann, man muss halt wissen, dass das etwas anderes ist wie in anderen Sprachen, aber das sind ja nur Begrifflichkeiten...)