Python Variablenfehler

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
UnSpeed
User
Beiträge: 6
Registriert: Samstag 28. Mai 2011, 14:42

Hey Leute,

ich bin noch relativ neu mit Python zugange, allerdings nicht mit Programmierung an sich, deshalb macht mich auch das folgende Problem ein bisschen stutzig:

Code: Alles auswählen

import sys, random

def menue():
	level_int = 5
	spielen = True
	while spielen:
		print "(N)eues Spiel\n(L)evelauswahl\n(A)nleitung\n(B)eenden"
		eingabe = str(raw_input(""))
		if eingabe == "n" or eingabe == "N":
			#Hier beginnt ein neues Spiel
			print level_int
			zahl = random.randint(0,level_int*10)
			print zahl
		elif eingabe == "l" or eingabe == "L":
			#Hier kommt die Levelauswahl
			print "Es gibt drei Level:\n(L)eicht\n(M)ittel\n(S)chwer"
			level = str(raw_input(""))
			if level == "l" or eingabe == "L":
				level_int = 1
			elif level == "m" or eingabe == "M":
				level_int = 5
			elif level == "s" or eingabe == "S":
				level_int = 10
			else:
				print "Fehlerhafte Eingabe. Gehe zu Hauptmenue"
			print level_int
			menue()
		elif eingabe == "a" or eingabe == "A":
			#Hier kommt die Anleitung
			print ""
		elif eingabe == "b" or eingabe == "B":
			#Hier wird das Spiel beendet
			spielen = False
		

print "Hallo hier ein kleines Spiel :D"
menue()
Wenn ich jetzt das Programm starte, dann funktioniert auch eigentlich alles, bis auf die Tatsache, dass egal was ich in der Levelauswahl eingebe level_int bei einem neuen Spiel immer 5 wird.
Denn wenn ich beispielsweise als Level "(L)eicht" auswähle, dann wird level_int auf 1 gesetzt. Starte ich aber danach ein neues Spiel, dann ich level_int wieder automatisch 5...

Syntaxfehler sind keine vorhanden und den Logikfehler sehe ich nicht -.-

Bitte helft mich weiter, ich verzweifel sonst noch :D

Danke,
UnSpeed
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo,

nun ja, sobald 'menue()' aufgerufen wird, wird 'level_int' auf 5 gesetzt.

Es dürfte auch kaum möglich sein, das Spiel über b/B zu beenden... :wink:

Noch ein Tipp zur Vereinfachung: Statt

Code: Alles auswählen

eingabe == 'b' or eingabe == 'B'
würde ich die entweder die eingabe gleich in einen Klein- oder Großbuchstaben umwandeln

Code: Alles auswählen

#entweder
eingabe = str(raw_input('Diese Eingabe wird klein: ')).lower()
#oder
eingabe = str(raw_input('Diese Eingabe wird GROSS: ')).upper()
dann musst Du nur auf einen Groß- oder eben einen Kleinbuchstaben prüfen, oder aber Du überprüfst die Eingabe so:

Code: Alles auswählen

eingabe in 'aA'
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
UnSpeed
User
Beiträge: 6
Registriert: Samstag 28. Mai 2011, 14:42

Mir ist bewusst, dass das beim Aufrufen der Funktion auf 5 gesetzt wird, aber die Variable wird ja innerhalb der Funktion geändert, und DAS ist meine Frage: WARUM ändert sich das trotzdem in 5?

Das mit der eingabe war auch nur eine Notlösung, denn der Befehl .lower() war mir bereits bekannt. Der Befehl in 'aA' da gegen nicht, danke ;)

Und doch, das Spiel lässt sich über b/B beenden, was ja auch logisch ist, weil dadurch die While-Schleife unterbrochen wird, die Funktion normal ausläuft und nach dem Funktionsaufruf nichts mehr folgt.


Leider weiß ich jetzt aber immer noch nicht, wie ich das eigentliche Problem beheben kann...
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

UnSpeed hat geschrieben:Und doch, das Spiel lässt sich über b/B beenden, ...
Stimmt. Mein Fehler, ich dachte, nach Eingabe von b/B geschieht dasselbe wie nach Eingabe von l/L, wo eben Dein Fehler steckt.
Damit meine ich, dass Du nach Eingabe von l/L die 'menue()' von neuem aufrufst und somit nicht zurück zum Schleifenanfang gehst, sondern eine neue Schleife startest. Mit anderen Worten:

Code: Alles auswählen

def menue():
    ...
    level = 5
    while spielen:
        ...
        if eingabe in 'lL':
            ...
            menue()
Da kannst Du l/L's auswählen, bis Du schwarz wirst oder irgendwann ein RuntimeError kommt, weil die maximale Rekursionstiefe erreicht ist.

Jetzt klarer?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
UnSpeed
User
Beiträge: 6
Registriert: Samstag 28. Mai 2011, 14:42

autsch xD

Allerdings, wenn ich den Code folgend umstelle, dann kann ich offenbar nicht auf die Variable "spielen" zugreifen:

Code: Alles auswählen

import sys, random

level_int = 5
spielen = True

def menue():
	while spielen:
		print "(N)eues Spiel\n(L)evelauswahl\n(A)nleitung\n(B)eenden"
		eingabe = str(raw_input(""))
		if eingabe == "n" or eingabe == "N":
			#Hier beginnt ein neues Spiel
			print level_int
			zahl = random.randint(0,level_int*10)
			print zahl
		elif eingabe == "l" or eingabe == "L":
			#Hier kommt die Levelauswahl
			print "Es gibt drei Level:\n(L)eicht\n(M)ittel\n(S)chwer"
			level = str(raw_input(""))
			if level == "l" or eingabe == "L":
				level_int = 1
			elif level == "m" or eingabe == "M":
				level_int = 5
			elif level == "s" or eingabe == "S":
				level_int = 10
			else:
				print "Fehlerhafte Eingabe. Gehe zu Hauptmenue"
			print level_int
			menue()
		elif eingabe == "a" or eingabe == "A":
			#Hier kommt die Anleitung
			print ""
		elif eingabe == "b" or eingabe == "B":
			#Hier wird das Spiel beendet
			spielen = False
		

print "Hallo hier ein kleines Spiel :D"
menue()
Dann kommt diese Fehlermeldung:

Traceback (most recent call last):
File "ratespiel.py", line 37, in <module>
menue()
File "ratespiel.py", line 5, in menue
while spielen:
UnboundLocalError: local variable 'spielen' referenced before assignment

Edit:
Hat sich erledigt, ich habe einfach die Variablen wieder reingetan und den Funktionsaufruf innerhalb der Funktion gelöscht.
Allerdings verstehe ich dir oben genannte Fehlermeldung noch nicht ganz...
Kann da vllt. bitte noch mal jmd. helfen?
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Du denkst in die falsche Richtung... :wink:

Die Frage ist doch, weshalb muss denn 'menue()' neu aufgerufen werden? Du befindest Dich doch in einer Schleife...
Das was Du machst nennt sich Rekursion, das heißt, eine Funktion ruft sich selbst auf. Macht man z. B. bei Sortieralgorithmen.
Du möchtest aber etwas ganz anderes, nämlich nach der Levelauswahl wieder zurück zur Eingabe. Und um zur Eingabe zurückzukehren musst Du doch nicht erneut 'menue()' aufrufen.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

UnSpeed hat geschrieben:Allerdings verstehe ich dir oben genannte Fehlermeldung noch nicht ganz...
Das liegt daran, dass der Name 'spielen' außerhalb der Funktion 'menue()' erstellt wurde und daher innerhalb von 'menue()' nicht gesehen werden kann.
Wenn du innerhalb einer Funktion auf Namen zugreifen möchtest, die außerhalb erstellt wurden, dann musst Du diese Namen an die Funktion übergeben. In Deinem Fall könnte das so aussehen:

Code: Alles auswählen

import sys, random

level = 5
spielen = True

def menue(level, spielen):
    ...

menue(level, spielen)
Dabei geschieht folgendes: In dem Modul, in dem sich 'menue()' befindet, wird 'level' an die Zahl 5 und 'spielen' an den Wert True gebunden.
Durch den Aufruf von 'menue(level, spielen)' wird die Zahl 5 und der Wert True an die Funktion 'menue()' übergeben und dort an die Namen 'level' und 'spielen' gebunden. Wenn Du nun innerhalb der Funktion die Werte von 'level' oder 'spielen' änderst, gilt das auch nur innerhalb der Funktion, auf Modulebene bleibt alles beim alten. Auch wenn das nicht ganz korrekt ist kann man sagen, dass die Namen 'level' und 'spielen' doppelt existieren, einmal auf Modul- und einmal auf Funktionsebene.
Wenn Du 'level' und 'spielen' auch auf Modulebene an die Werte, die innerhalb der Funktion gelten, angleichen möchtest, könntest Du das z. b. so erreichen:

Code: Alles auswählen

import sys, random

level = 5
spielen = True

def menue(level, spielen):
    ...
    return (level, spielen)

level, spielen = menue(level, spielen)
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
UnSpeed
User
Beiträge: 6
Registriert: Samstag 28. Mai 2011, 14:42

Danke, ich weiß, was Rekursion ist :)

Ich programmiere schon seit einigen Jahre und kenne grundspezifisch auch viele Strukturen (wie eben beispielsweise Rekursion :D).

Ich brauchte nur die Information, dass Variablen, die ausserhalb definiert worden sind, nicht ohne weiteres in eine Funktion übernommen werden können.
Da liegt übrigens einer der Unterschiede zu Java ;)

Damit hat sich dieses Thema erledigt --> Danke :D
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

UnSpeed hat geschrieben:Da liegt übrigens einer der Unterschiede zu Java
Meines Halbwissens nach gilt das bei Java aber nur für statische Variablen, die es in dieser Form bei Python nicht gibt. Alle anderen Variablen sind ebenfalls sehr wohl an den Namensraum ihrer Deklaration gebunden.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
UnSpeed
User
Beiträge: 6
Registriert: Samstag 28. Mai 2011, 14:42

mutetella hat geschrieben:
UnSpeed hat geschrieben:Da liegt übrigens einer der Unterschiede zu Java
Meines Halbwissens nach gilt das bei Java aber nur für statische Variablen, die es in dieser Form bei Python nicht gibt. Alle anderen Variablen sind ebenfalls sehr wohl an den Namensraum ihrer Deklaration gebunden.

mutetella
Das ist teilweise richtig ;)
In Java gibt es immer eine so genannte Klasse, diese Klasse ist sozusagen die Hülle eines Objektes.
Werden nun Variablen in dieser Klasse deklariert beispielsweise so:

Code: Alles auswählen

boolean entscheidung = false;
so sind sie auch in alles Methoden dieses Objektes verfügbar.
Nicht jedoch, will man sie aus einer Methode eines anderen Objektes aufrufen, dafür muss sie in der Tat static (statisch) sein :)

Aber hier gehts ja nicht um Java, deshalb bin ich jetzt mal still ;)

Edit:
BlackJack hat mich freundlicher Weise darauf aufmerksam gemacht, dass das, was ich Konstruktor nannte, natürlich nicht der Konstruktor, sondern die Klasse ist.
(Geistige Umnachtung :D)
Deshalb habe ich 'Konstruktor' in Klasse geändert.
Vielen Dank an dieser Stelle :)
Zuletzt geändert von UnSpeed am Sonntag 29. Mai 2011, 09:43, insgesamt 2-mal geändert.
BlackJack

@UnSpeed: Das ist jetzt das erste mal das ich *das* bei Java jemanden den Konstruktor nennen sehe. Das würde ich eher die Deklaration der Exemplarvariablen nennen. Denn die Variablen die man in einem Konstruktor deklariert sind auch bei Java nur innerhalb des Konstruktors sichtbar, wie bei jeder anderen Methode auch.

Code: Alles auswählen

class Test {
    int a = 42;
    Test() {
        int b = 23;  // Deklaration im Konstrukor -- nicht ausserhalb sichtbar.
    }
}
Auch wenn Du weisst was Rekursion ist — in Java wäre es ebenfalls keine gute Idee sie auf diese Weise zu verwenden. Denn auch Java garantiert keine Optimierung von Endrekursion und würde da *sehr* wahrscheinlich irgendwann mit einem übergelaufenen Aufrufstapel das Programm abbrechen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

mutetella hat geschrieben:

Code: Alles auswählen

#entweder
eingabe = str(raw_input('Diese Eingabe wird klein: ')).lower()
#oder
eingabe = str(raw_input('Diese Eingabe wird GROSS: ')).upper()
@mutetella: `raw_input` liefert Dir immer einen String zurück - der Aufruf von `str` ist also überflüssig.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Hyperion:
Ich hatte gehofft, dass das niemand mehr bemerkt... :wink:
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
UnSpeed
User
Beiträge: 6
Registriert: Samstag 28. Mai 2011, 14:42

@ Blackjack:

Sicher, du hast recht --> geändert ;)
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Zur Übung für mich und auch, weil ich mein Kalenderprogramm heute nicht mehr sehen konnte :( habe ich ein wenig herumgespielt, die Eingabeschleife etwas entzerrt und das, was da IMHO nicht unbedingt hineingehört, ausgelagert:

Code: Alles auswählen

import random

LEVEL = {'L': 1, 'M': 5, 'S': 10}

def menu(level=5):
    while True:
        choice = make_choice(
            'Hauptmenu',
            '(N)eues Spiel - (L)evelauswahl - (A)nleitung - (E)nde',
            'NLAE', False)
        if choice == 'N':
            #Hier beginnt ein neues Spiel
            gamestart(level)
        elif choice == 'L':
            #Hier kommt die Levelauswahl
            choice = make_choice(
                'Levelauswahl',
                '(L)eicht - (M)ittel - (S)chwer',
                'LMS')
            if choice != 'X':
                level = LEVEL[choice]
        elif choice == 'A':
            #Hier kommt die Anleitung
            manual()
        elif choice == 'E':
            #Hier wird das Spiel beendet
            break

def make_choice(label, text, valid, cancel=True):
    if cancel:
        prompt = '|Ihre Auswahl oder \'X\' zum Abbrechen > '
        valid = valid + 'X'
    else:
        prompt = '|Ihre Auswahl > '
    choice = ''
    while True:
        print '|{0}'.format(label)
        print '|{0}'.format('-'*len(text))
        print '|{0}'.format(text)
        choice = raw_input(prompt).upper()
        print
        if choice in valid:
            return choice
        print '>>>>  FEHLERHAFTE   EINGABE  <<<<'
        print 'Bitte treffen Sie Ihre Auswahl aus'
        print '{0:^34}'.format(valid)

def gamestart(level):
    number = random.randint(0, level*10)
    print 'Level {0}: {1}'.format(level, number)

def manual():
    print 'So oder so...'

if __name__ == '__main__':
    menu()
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

UnSpeed hat geschrieben:In Java gibt es immer eine so genannte Klasse, diese Klasse ist sozusagen die Hülle eines Objektes.
Werden nun Variablen in dieser Klasse deklariert beispielsweise so:

Code: Alles auswählen

boolean entscheidung = false;
so sind sie auch in alles Methoden dieses Objektes verfügbar.
Nicht jedoch, will man sie aus einer Methode eines anderen Objektes aufrufen, dafür muss sie in der Tat static (statisch) sein :)
Du meinst sicher public und nicht static ;-)
Das Leben ist wie ein Tennisball.
Antworten