Neuling,Fragen zu lokalen und globalen Variablen

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
0John0
User
Beiträge: 12
Registriert: Donnerstag 8. Februar 2007, 12:17

Also ich hab mir ein einstiegsbuch gekauft und arbeite es durch.Bis jetzt habe ich alles verstanden. Doch diese sache mit den lokalen und globalen variabeln hab ich noch nicht ganz durchblickt.
Hier ein Programmbeispiel:


Code: Alles auswählen

from xturtle import *

def quadrat (laenge):
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    seite = seite + 50
    print seite
    

seite = 250
quadrat (100)
Wenn ich das ausführen lassen bekomme ich folgende Fehlermeldung

Code: Alles auswählen

seite = seite + 50
UnboundLocalError: local variable 'seite' referenced before assignment
ist mir auch klar warum man müsste am anfang "seite" als global bezeichnen dann würde es funktionieren

warum aber funktioniert das?

Code: Alles auswählen

from xturtle import *

def quadrat (laenge):
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    print seite
    

seite = 250
quadrat (100)
die ausgabe lautet dann 250. was ich daran nicht verstehe ist,in diesem Fall hat das Programm ja auch die variable "seite" von ausserhalb geholt und deren wert angezeigt.

warum funktionert das nicht auch mit dem oberen Programmbeispiel?Da könnte Python ja auch die variable "seite" holen und 50 dazu zählen?
BlackJack

Die Entscheidung ob ein Name lokal oder global ist wird, wenn man kein ``global`` benutzt, danach entschieden ob der Name in der Funktion nur referenziert wird, wie in Deinem zweiten Beispiel, oder ob man in der Funktion den Namen an ein Objekt bindet, wie im ersten Beispiel.
Nirven
User
Beiträge: 130
Registriert: Mittwoch 10. Mai 2006, 08:18
Wohnort: Bremerhaven

Und grundsätzlich sollte man keine globalen Variablen benutzen. Dann hat man solche Probleme auch nicht...

Wofür auch immer 'seite' in dem Beispiel überhaupt gut ist, übergib es besser der Funktion beim Aufruf.

Code: Alles auswählen

from xturtle import *

def quadrat (laenge, seite):
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    fd(laenge)
    lt(90)
    seite = seite + 50
    print seite
   

seite = 250
quadrat (100, seite)
0John0
User
Beiträge: 12
Registriert: Donnerstag 8. Februar 2007, 12:17

ah jetzt ist klar!danke blackjack das war kurz und bündig aber genau das was ich wissen wollte :)
@nirven
wie ich oben erwähnt habe ist es nur ein beispielprogramm, ist mir schon klar das "seite"keinen grossen Sinn macht wollte nur das mit diesen Variablen ausprobieren
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

Hmm... das scheint tatsächlich so zu sein, aber einerseits verstehe ich die Logik dahinter nicht und zum anderen ist die Fehlermeldung dann nicht so aussagekräftig, wie ich es von Python gewohnt bin bzw. ziemlich irreführend.
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
BlackJack

Es wurde versucht eine Lösung zu finden die möglichst wenig Deklarationen verlangt und im allgemeinen Fall keine "Probleme" macht. Wobei der allgemeine Fall ist, dass man globale Namen referenziert, ihnen aber nichts zuweist. Also wird grundsätzlich erst einmal alles mit einer Zuweisung als lokal angesehen und alles was nur referenziert wird und nicht in der Argumentliste steht, als global.

Wie hätte man das sonst lösen sollen?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Diese Seite ist zu dem Thema vielleicht ganz interessant.
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

Was mich ein wenig irritiert ist der "Schlingerkurs". Für gewöhnlich hat man bei Zuweisungen eine Auswertung von rechts nach links. Daher kann der Programmierer auch Dinge schreiben wie x = x + 2. Ein Mathematiker schreibt einfach ein f dahinter und gut. Also was hier passiert ist doch, dass zuerst der rechte Teil ausgewertet wird, dem Wert von x wird 2 hinzuaddiert und das wieder dem Namen x zugewiesen. Bei Python wird aber offenbar zuerst links nachgesehen, ob der lokale Name x schon existiert, ansonsten wird er erzeugt und dann der rechte Teil ausgewertet, dessen Ergebnis dann dem Namen links zugewiesen wird.

Code: Alles auswählen

>>> def lvl0():
... 	x = 10
... 	def lvl1():
... 		print x
... 		def lvl2():
... 			print x
... 		lvl2()
... 	lvl1()
... 	print x
... 	
>>> lvl0()
10
10
10
Soweit so gut. Aber:

Code: Alles auswählen

>>> def lvl0():
... 	x = 10
... 	def lvl1():
... 		print x
... 		def lvl2():
... 			x = x + 2
... 			print x
... 		lvl2()
... 	lvl1()
... 	print x
... 	
>>> lvl0()
10
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 9, in lvl0
  File "<interactive input>", line 8, in lvl1
  File "<interactive input>", line 6, in lvl2
UnboundLocalError: local variable 'x' referenced before assignment

Auch unter Berücksichtigung der LEGB-Regel wäre das erwartete Ergebnis IMHO
10
12
10
gewesen.
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Die Lösung ist einfach:

Code: Alles auswählen

>>> x = 42
>>> def foo():
...   x += 2
... 
>>> dis.dis(foo)
  2           0 LOAD_FAST                0 (x) # <-- !!!
              3 LOAD_CONST               1 (2)
              6 INPLACE_ADD         
              7 STORE_FAST               0 (x)
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE        
>>> def bar():
...   a = 0
...   a += x
... 
>>> dis.dis(bar)
  2           0 LOAD_CONST               1 (0)
              3 STORE_FAST               0 (a)

  3           6 LOAD_FAST                0 (a)
              9 LOAD_GLOBAL              0 (x) # <-- !!!
             12 INPLACE_ADD         
             13 STORE_FAST               0 (a)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        
Wie man am Bytecode schön erkennen kann läd er im ersten Fall die Variable nicht aus dem globalen Namespace, sondern aus dem Fast Namespace (lokale Variablen, stark vereinfacht gesagt). Für Python kann ein Name entweder nur global oder nur lokal sein, aber niemals beides das gleiche in der selben Funktion.
TUFKAB – the user formerly known as blackbird
BlackJack

N317V hat geschrieben:Also was hier passiert ist doch, dass zuerst der rechte Teil ausgewertet wird, dem Wert von x wird 2 hinzuaddiert und das wieder dem Namen x zugewiesen. Bei Python wird aber offenbar zuerst links nachgesehen, ob der lokale Name x schon existiert, ansonsten wird er erzeugt und dann der rechte Teil ausgewertet, dessen Ergebnis dann dem Namen links zugewiesen wird.
Nein zur Laufzeit wird erst Rechts ausgewertet und dabei festgestellt, das der *lokale* Name `x` an gar kein Objekt gebunden ist und damit auch ``x + 2`` nicht berechnet werden kann.

Das der Name `x` in `lvl2()` ein lokaler Name ist wurde schon *viel* früher entscheiden, nämlich beim Übersetzen wie blackbird gezeigt hat.

Du fragtest nach der Logik: Stellen wir uns mal für einen Moment vor es wäre anders und `x` würde sich auf das "äussere" `x` beziehen. Das würde dann bedeuten, dass in folgender Funktion:

Code: Alles auswählen

def f():
    x = 42
das `x` solange lokal ist, bis jemand auf die Idee kommt im umgebenen Sichtbarkeitsbereich, ob das nun das Modul ist oder eine umschliessende Funktionsdefinition, auch den Namen `x` zu benutzen. Da wäre das Chaos vorprogrammiert. Alleine ein ``from spam import *`` könnte ein massiv anderes Verhalten des Codes zur Folge haben, ohne das man mit einem statischen Blick auf den Quelltext des importierenden Moduls sagen könnte warum. Man müsste sich ständig Gedanken machen das man von "aussen" keine lokalen Namen in Funktionen benutzt, was das Konzept von lokalen Namen an sich völlig nutzlos macht.
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

@BlackJack:

Nein, es würde rechts der Wert 42 genommen, dann ein lokaler Name x erzeugt, der aufgrund von LEGB alle äußeren x lokal überlagerte und dieser dem Wert 42 zugewiesen. Sobald eine lokale Zuweisung erfolgte wäre das x vor äußeren Einflüssen gefeit, aber eben erst nach der ersten Zuweisung, ergo auch nach dem Auswerten des Ausdrucks auf der rechten Seite. Wo wäre das Problem?
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
BlackJack

Okay, ich bin davon ausgegangen das die Entscheidung lokal oder global keine dynamische ist und ein Name in einer Funktion entweder das eine oder das andere ist.

Sowohl wie es jetzt gelöst ist, als auch bei Deinem Vorschlag muss man sehen ob ein Namen in der Funktion gebunden wird oder nicht, um zu sehen ob er lokal oder global ist. Dein Vorschlag hat dabei den Nachteil, das man auch den Programmfluss kennen muss um zu wissen wann und wo der Name lokal ist und wo global.

Implementierungstechnisch würde es wahrscheinlich langsamere Funktionen zur Folge haben, weil man lokal dann nicht mehr mit einem Array arbeiten könnte, und die Auswirkungen auf Closures müsste man auch mal überdenken.
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

BlackJack hat geschrieben:Dein Vorschlag hat dabei den Nachteil, das man auch den Programmfluss kennen muss um zu wissen wann und wo der Name lokal ist und wo global.
Muss man jetzt auch:

Code: Alles auswählen

>>> def lvl0():
... 	x = 42
... 	def lvl1():
... 		print x
... 		def lvl2():
... 			print x
... 			x = 666
... 		lvl2()
... 	lvl1()
... 	print x
... 	
>>> lvl0()
42
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 10, in lvl0
  File "<interactive input>", line 9, in lvl1
  File "<interactive input>", line 6, in lvl2
UnboundLocalError: local variable 'x' referenced before assignment
Wieso könnte man kein Array mehr verwenden? Solange die LEGB-Regel angewandt wird, ist es doch egal. Schlimmstenfalls schleppe ich einen im Funktions-Kontext nicht mehr benötigten globalen Wert mit. Genausogut könnte man den aber auch rausschmeißen, oder? Ich muss aber zugeben, dass ich mich mit der Implementierung eigentlich noch gar nicht befasst habe.
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
BlackJack

So wie es jetzt ist muss man den Programmfluss nicht kennen, es reicht wenn man sich den Quelltext anschaut und man kann ganz klar sagen, wann und wo jeder Name lokal oder global ist. Sowie eine Zuweisung zu lesen ist, weiss man das der entsprechende Name in der ganzen Funktion lokal ist, zu jedem Zeitpunkt, bei jeder Ausführung. Bei Deinem Vorschlag kann ein Name nicht nur an verschiedenen Stellen global oder lokal sein, sondern auch an ein und derselben Stelle kann er, je nach Programmablauf global oder lokal sein.

Mit dem Array habe ich mich wohl getäuscht, das kann man weiterhin benutzen.
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

Irgendwie muss ich da nochmal drüber schlafen.

Code: Alles auswählen

>>> class foo():
... 	x = 42
... 	def out(self):
... 		print x
... 		
>>> F = foo()
>>> F.out()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 4, in out
NameError: global name 'x' is not defined
>>> x = 666
>>> F.out()
666
>>> 
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
BlackJack

Falls Du 42 als Ausgabe haben wolltest, dann fehlt ein ``self`` vor dem `x`. Es wird weder automatisch im Objekt oder seiner Klasse nachgeschaut.
N317V
User
Beiträge: 504
Registriert: Freitag 8. April 2005, 13:23
Wohnort: München

Dass es mit self geht, weiß ich auch. Aber eigentlich ist die Klassendefinition doch der umgebende Block. Steht aber, wie ich gerade gesehen hab, auch so in der Doku:
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods.
Es gibt für alles eine rationale Erklärung.
Außerdem gibt es eine irrationale.

Wie man Fragen richtig stellt
Antworten