Finde meinen Fehler einfach nicht

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.
Matty265
User
Beiträge: 7
Registriert: Sonntag 3. Januar 2010, 22:30

Hallo,
ich habe ein kleines Terminal-RPG geschrieben, bzw bin dabei, und finde einfach meinen Fehler zum verecken nicht.
Ich wusste nicht wo ich mich sonst hätte melden können, hoffe ich bin hier richtig

Code: Alles auswählen

# -*- coding: iso-8859-1 -*-
from random import *

def fusssoldat():
        geg_leben = randint(20,25)
        return geg_leben
def schlag():
        cache1 = randint(1,10)
        if cache1 == 2:
                haerte = 0
                return haerte
        else:
                haerte = randint(15, 20)
                return haerte

def kritisch():
        cache1 = randint(1, 5)
        if cache1 == 2:
                haerte = randint(20,26)
                return haerte
        else:
                haerte = 0
                return haerte
def runde():
        print "Ein Fußsoldat!!!"
        print "Lebenspunkte: ", cache
        x = int(raw_input("1: Normaler Schlag, 2: Kritischen Schlag versuchen "))
        if x == 1:
                staerke = schlag()
                cache = seinleben-staerke
                print "Übrige Lebenspunkte: ", cache
        elif x == 2:
                staerke = kritisch()
                cache = seinleben-staerke
                print "Übrige Lebenspunkte: ", cache


eigenes_leben = 100
hilfe = 0
cache = fusssoldat()

while cache > 0:
       runde()
        
Hier die Fehlernachricht:
Bild
Zuletzt geändert von Matty265 am Sonntag 3. Januar 2010, 22:36, insgesamt 1-mal geändert.
tordmor
User
Beiträge: 100
Registriert: Donnerstag 20. November 2008, 10:29
Wohnort: Stuttgart

Woher kommt seinleben in Zeile 30?
http://www.felix-benner.com
Matty265
User
Beiträge: 7
Registriert: Sonntag 3. Januar 2010, 22:30

Ist gefixt, da sollte auch "cache" hin.
Trotzdem immernoch der gleiche Fehler
start_with_python
User
Beiträge: 41
Registriert: Samstag 20. Juni 2009, 18:12

Ganz einfach: Fehlermeldung lesen und verstehen!

"UnboundError: local variable 'cache' referenced before assignment"
:arrow: Variable 'cache' benutzt obwohl sie noch nicht existiert.
Zudem sagt die Fehlermelung, dass das ganze in Zeile 26 in der Funktion "runde".
Grüße[b]
start_with_python[/b]

Lust auf [url=https://www.dropbox.com/referrals/NTE5OTQ5Mjk5]DropBox[/url]? (RefLink)
Matty265
User
Beiträge: 7
Registriert: Sonntag 3. Januar 2010, 22:30

Schon klar, aber sie wird doch mit "cache = fusssoldat()" generiert oder nicht?
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Matty265 hat geschrieben:Schon klar, aber sie wird doch mit "cache = fusssoldat()" generiert oder nicht?
Ja, ABER: Da du in der Funktion runde() im weiteren Verlauf eine Zuweisung an einen Bezeichner namens cache vornimmst, wird eine solche Variable in den lokalen Namensraum eingeführt, die den globalen Bezeichner "cache" überschattet. Somit versuchst du, auf die lokale Variable cache zuzugreifen, bevor sie definiert wurde ...
start_with_python
User
Beiträge: 41
Registriert: Samstag 20. Juni 2009, 18:12

Jau, aber nur lokal. Du musst die Daten weitergeben. So bswp:

Code: Alles auswählen

def runde(deinedaten):
    cache = deinedaten


cache=fusssoldat()
runde(cache)
[/code]
Grüße[b]
start_with_python[/b]

Lust auf [url=https://www.dropbox.com/referrals/NTE5OTQ5Mjk5]DropBox[/url]? (RefLink)
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Das ist in der Tat ein seltsames Verhalten. In Zeile 30 bzw 34 erzeugst du eine lokale Variable "cache". Wenn der Interpreter in Zeile 26 angekommen ist, wurde diese allerdings noch nicht erzeugt (lokal). Sie existiert aber global. Das erwartete Verhalten wäre imho, dass er die globale Variable nimmt. Tut er aber nicht.

Es scheint, als würde der Parser beim Interpretieren einer Zeile schon von den nächsten Zeilen Bescheid wissen. Weird.
Matty265
User
Beiträge: 7
Registriert: Sonntag 3. Januar 2010, 22:30

Ah, ok alles klar.
Nur wie könnte ich das Problem lösen?
Das Problem ist ja, das auf cache die aktuellen Lebensdaten gespeichert sind, welche ja immer weiter benutzt werden sollen. Und da die Funktion in einer Schleife ist, sollten diese Daten ja ausserhalb der Funktion definiert werden, da ja sonst die Daten immerwieder resettet werden
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Code: Alles auswählen

# -*- coding: iso-8859-1 -*-
from random import randint

fusssoldat = lambda: randint(20,25)

def schlag():
    #chance von 1 zu 10 nicht zu treffen
    if not randint(0,9):
        return 0
    else:
        return randint(15, 20)

def kritisch():
    #chance von 1 zu 5 zu treffen
    if not randint(0,4):
            return randint(20,26)
    return 0

def runde(f_leben):
    print "Ein Fußsoldat!!!"
    print "Lebenspunkte: ", f_leben
    x = int(raw_input("1: Normaler Schlag, 2: Kritischen Schlag versuchen "))
    if x == 1:
        staerke = schlag()
    elif x == 2:
        staerke = kritisch()
        
    f_leben = f_leben-staerke
    print "Übrige Lebenspunkte: ", f_leben
    return f_leben

def game():
    eigenes_leben = 100
    hilfe = 0
    f_leben = fusssoldat()

    while f_leben > 0:
        f_leben = runde(f_leben)


game()
edit: Hab mal angefangen das neu aufzuziehen: http://paste.pocoo.org/show/161762/
Zuletzt geändert von jbs am Sonntag 3. Januar 2010, 23:31, insgesamt 1-mal geändert.
[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:

@OP: Du solltest Dir mal PEP8 durchlesen (s. auch Sig von jbs ;-) ). Die Einrückung sollte eigentlich 4 Spaces sein - bei Dir sieht es nach 8 aus.

Außerdem solltest Du Dir mehr Gedanken über die Bennenung von Objekten und Bezeichnern machen. "schlag" würde eher auf eine Klasse als auf eine Funktion hinweisen! Gleiches gilt für "fussoldat" - wieso nicht "create_footsoldier()"? Gleiches gilt für "schlag" und "kritisch". Auch da würde sich ein "do_xyz" besser machen oder eben ein richtiges passendes Verb. Das mag Dir im Moment nicht so wichtig vorkommen, aber bei größeren Programmen und nach einiger Zeit wieder angefasster Software macht sich so etwas bezahlt!

Zudem solltest Du auch auf Dokumentation achten. Was macht eine Funktion? Was liefert sie zurück? Was für Parameter erwartet sie? (String, Iterable, Dictionary, File-like-Object, ...)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Erstens, man kann auch aus einer DOS-Konsole kopieren oder den Fehler einfach abschreiben. Dabei fällt einem dann vielleicht ja auch auf, dass der Python-Interpreter genau auf die fehlerhafte Zeile zeigt und sogar (auf Englisch) den konkreten Fehler meldet. Aber das haben ja schon andere angesprochen.

Die Regel, das eine Variable als lokal zählt, wenn *irgendwo* in einer Funktion eine Zuweisung an diese Variable steht, ist nun mal, wie Python (Ruby übrigens auch) funktioniert. Seltsam ist das nicht.

Auf die Frage, wie man das Problem löst: Am besten ohne veränderbare globale Variablen. Ich habe auch mal etwas gebaut, das man unter http://paste.pocoo.org/show/161775/ findet. Der Zufall will, dass ich gerade an so etwas ähnlichem bastle.

Ein paar Erklärungen: Ich ziehe `randrange` dem `randint` vor, weil sich dies so wie andere Range-Funktionen verhält und man sich nicht merken muss, wann denn nun das Ende Teil des Intervalls ist und wann nicht. Geschmackssache. Die Funktion `d` würfelt 1W6 (also einen 6-seitigen Würfel). Die Hilfsfunktion `critical` entscheidet, ob ein Wurf von 3W6 als kritischer Treffer gewertet wird, was passiert, wenn ein Pasch dabei ist. Die Klasse `Character` repräsentiert die Kämpfer. Man könnte sich diverse Attribute vorstellen, doch ich habe mich auf den Namen, einen Wert für die Verteidigung (was eine Rüstung repräsentieren könnte), die Waffe (wovon es nur eine gibt) und Lebenspunkte entschieden. Waffen repräsentiere ich mit der Klasse `Weapon`. Sie haben einen Namen und eine Funktion, die den zufälligen Schaden bestimmt. Ein Speer habe den Schaden 1W6+3, ein Breitschwert macht 2W6 Punkte Schaden. Die Methode `Character.attack` lässt den Charakter einen anderen angreifen. Ein kritischer Treffer verdopple einfach den Schaden. Hier könnte man (wie bei meinem Regelvorbild, dem Dragon Age RPG, natürlich auch andere Dinge machen, um den Kampf etwas abwechslungsreicher zu gestalten). Ein Charakter ist tot, wenn die Lebenspunkte 0 erreichen. Die Klasse `Combat` hat eine Methode `do` (was ein total schlechter Name ist), die einen Kampf bis zum Tod einer der beiden beteiligten Parteien durchführt. Das müsste jetzt nicht unbedingt eine Klasse sein, oder hier würde auch eine Funktion ausreichen, aber will man von 2 Parteien auf N gehen oder das ganze noch stärker parametrisieren, zeigt die Erfahrung, dass auch hier eine Klasse sinnvoll ist.

Thoma hat übrigens gute Chancen gegen zwei Fußsoldaten, verliert aber regelmäßig gegen drei.

Stefan
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Hyperion: Ob überall "make_", "create_", "get_", "do_", ... davorsteht, spielt IMHO eine eher untergeordnete Rolle, wenn die Namen nicht das beschreiben, was die Funktion tut, bzw das Konzept eher schlecht aufgebaut ist. jbs hat da schon einige Ansätze in seinem Edit geliefert, auch wenn ich persönlich noch ein paar Dinge anders machen würde.
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Ich hatte `randrange` total vergessen :(.

@sma: Wie hast du denn vor die Typen der Feinde zu verwalten?
[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]
BlackJack

@Dauerbaustelle: Der Parser interpretiert nichts sondern ist noch vor dem Compiler, den er mit den geparsten Token aus dem Quelltext füttert. Und bevor ein Modul ausgeführt wird, werden erst einmal alle enthaltenden Klassen, Funktionen, und Methoden in Bytecode übersetzt. Und bei *diesem* Schritt wird auch entschieden was lokal und was "global" ist. Wenn in einer Funktion eine Zuweisung an einen Namen passiert, dann ist der Name lokal, es sei denn er wurde als ``global`` deklariert (oder ``nonlocal`` bei Python 3).

Letztendlich sind beide Verhalten verwirrend. Denn wenn `cache` in einer Funktion zweimal vorkommt, aber in einem Fall auf einen anderen Namensraum zugegriffen wird, als in dem anderen, dann ist das sicher auch nicht besonders durchschaubar. Noch interessanter wird es, wenn dann noch bedingte Ausführung oder Schleifen ins Spiel kommen. Beispiele:

Code: Alles auswählen

a, b = 47, 11
def spam():
    if blah:
        a = 42
    print a
    for i in xrange(23):
        print b
        b = ham(i)
Ob die ``print``-Anweisungen auf die Modulebene oder lokal zugreifen, würde jetzt von einer Bedingung bzw. von der Nummer der Schleifeniteration abhängen. Wäre IMHO auch nicht besser.

Die jetzige Implementierung hat den Vorteil, dass die Namen und die Anzahl der lokalen Namen dem Compiler schon bekannt sind, und dass er deshalb Bytecode erzeugen kann, bei dem nicht mehr über den Namen in einem Dictionary nach den Objekten gesucht werden muss, sondern dass die Objekte zu den Namen in einem Array abgelegt werden, und der Zugriff über feste Indices passiert. Das ist schneller.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

BlackJack hat geschrieben:@Dauerbaustelle: Der Parser interpretiert nichts sondern ist noch vor dem Compiler, den er mit den geparsten Token aus dem Quelltext füttert.
Ich weiß doch, ich weiß doch, das war nur ein Wortmismatch :-D
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

snafu hat geschrieben:@Hyperion: Ob überall "make_", "create_", "get_", "do_", ... davorsteht, spielt IMHO eine eher untergeordnete Rolle, wenn die Namen nicht das beschreiben, was die Funktion tut,...
Sicher. Nur gibt es eben oftmals kein explizites Verb, was die "Funktion" einer Funktion beschreibt. Daher kann man sich in diesen Fällen gut mit der Kombi aus Verb und Infinitiv helfen. Ich hatte das ja anhand des Beispiels versucht zu erläutern. Ich gebe zu, dass der explizite Hinweis auf die Sinnhaftigkeit der Bennenung bei mir fehlt - aber den lieferst Du ja nun nach ;-)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

In der Regel ist es eine gute Idee, Methoden mit Verben zu beschreiben. Ausnahmen bestätigen die Regel. In Smalltalk beispielsweise schreibt man Getter- und Setter-Methoden wie in Python die Attribute direkt ohne Verb. Dafür benutzt man dort für Boolsche Attribute gerne ein "is" oder "can" während das in Ruby (oder Scheme) meist durch ein "?" am Ende angedeutet wird. In Java wiederum hat sich "get" und "set" für Accessor-Methoden durchgesetzt.

Aber wenn ich z.B. eine Klasse "CharacterCreator" hätte, die Methoden hat, mit denen ich initialisierte Objekte wie z.B. Fußsoldaten erzeugen kann, dann würde ich dort dennoch "create_foot_soldier" und nicht einfach nur "foot_soldier" als Methodennamen wählen, selbst wenn das dadurch ein bisschen redundant wird. Doch das "create" deutet an, dass jeder Aufruf ein neues Objekt erzeugt und somit die Methode (im Gegensatz zu einem Getter) einen Seiteneffekt hat.

Nicht 100% passend aber Lesenswert in diesem Fall auch http://steve-yegge.blogspot.com/2006/03 ... nouns.html

Stefan
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

jbs hat geschrieben:@sma: Wie hast du denn vor die Typen der Feinde zu verwalten?
Was meinst du mit Typen? Die Klassen? Oder verschiedene Arten wie Goblin-Bogenschützen, Elfenreiter oder Orkwache?

Stefan
Benutzeravatar
jbs
User
Beiträge: 953
Registriert: Mittwoch 24. Juni 2009, 13:13
Wohnort: Postdam

Die verschiedenen Arten.
[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]
Antworten