Hier die Konstanten in Python... :-)

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
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

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
Zuletzt geändert von alpha am Freitag 11. Dezember 2009, 20:48, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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 :-)
Zuletzt geändert von Hyperion am Freitag 11. Dezember 2009, 18:41, insgesamt 1-mal geändert.
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

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.
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

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...
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Du kannst lesend auf sie zugreifen. Willst du sie verändern, musst du `global` benutzen.

Das ist aber dreckig und sollte vermieden werden.
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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...
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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.
Bottle: Micro Web Framework + Development Blog
crs
User
Beiträge: 42
Registriert: Dienstag 14. Juli 2009, 13:24

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
alpha
User
Beiträge: 195
Registriert: Freitag 23. Mai 2003, 23:24
Wohnort: Ulm

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?
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Naja, warum steht oben.
Eine Konstante ist aber etwas anderes.
[url=http://wiki.python-forum.de/PEP%208%20%28%C3%9Cbersetzung%29]PEP 8[/url] - Quak!
[url=http://tutorial.pocoo.org/index.html]Tutorial in Deutsch[/url]
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

jbs hat geschrieben:Eine Konstante ist aber etwas anderes.
Wie definierst du dann eine Konstante, anstatt auf Modul-Ebene?
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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.
Bottle: Micro Web Framework + Development Blog
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Defnull hat geschrieben:a kann manipuliert werden -> a ist keine Konstante.
Wie macht man es denn dann?
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

In Python? Gar nicht. Allenfalls durch Konvention.
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. :-)
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

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...)
Antworten