name 'item' is not defined

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.
Milkymalk
User
Beiträge: 12
Registriert: Freitag 11. Mai 2012, 22:22

Hallo,
Ich fühle mich gerade ziemlich dumm... wahrscheinlich sehe ich den Wald vor lauter Bäumen nicht.
Dieser Code produziert den Fehler "name 'item' is not defined" in der letzten Zeile. Wie kann das sein, wenn ich 'item' doch nicht zuweise, sondern ihm selbst einen Wert zuweisen will?
Ich habe mich auch nicht verlesen - es ist 'item' und nicht 'items'.

Code: Alles auswählen

    items = 11
    item_names = ['Werkzeug', 'Öl', 'Holzbalken', 'Korb', 'Stoff', 'Kreide', 'Seil', 'Holzeimer', 'Harke', 'Axt', 'Lederbeutel']
    item_costs = {'Werkzeug': 20, 'Öl': 4, 'Holzbalken': 6, 'Korb': 3, 'Stoff': 8, 'Kreide': 1, 'Seil': 4, 'Holzeimer':7, 'Harke': 5, 'Axt': 8, 'Lederbeutel': 3}
    
    class Iteminfo:
        def __init__(self, id, hasitem=False):
            self.name = item_names[id]
            self.cost = item_costs[self.name]
            self.hasitem = hasitem

    item[x] = [Iteminfo(x) for x in range(items)]
Daran ändert sich auch nichts, wenn ich stattdessen schreibe:

Code: Alles auswählen

for x in range(items):
    item[x] = Iteminfo(x)
Zuletzt geändert von Milkymalk am Mittwoch 16. Mai 2012, 13:40, insgesamt 1-mal geändert.
deets

du hast halt kein "item". Du hast items - was eine zahl ist.

item = [ItemInfo(x) for x in range(items)]


sollte gehen. Aber insegsamt ist das was du da vorhast ziemlich schlecht programmiert. Statt eine Laufvariable x zu kreieren, die dann muehselig in einer globalen(!!!!) Variable "item_names" aufgeloest wird, und dann auch noch die Kosten aus einer ebenso globalen variable zu holen, solltest du einfach nur deine item_costs nehmen:

items = [ItemInfo(name, cost) for name, cost in item_costs.iteritems()]

Spart dir items (die zahl), item_names und ItemInfo ist auch besser programmiert (natuerlich musst du das umschreiben, so das es nur die Argument nimmt, nicht nochmal nachschlaegt).
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Milkymalk hat geschrieben:Wie kann das sein, wenn ich 'item' doch nicht zuweise, sondern ihm selbst einen Wert zuweisen will?
Richtig, du greifst nicht schreibend auf `item` zu, wohl aber lesend. Wie sonst, solltest du denn in `item` schreiben koennen, wenn du nicht weisst wo oder was `item` ist?

Du willst aber eigentlich das hier nutzen:

Code: Alles auswählen

item = [Iteminfo(x) for x in range(items)]
Uebrigens:

Code: Alles auswählen

item_names = item_costs.keys()
Und schon bist du von inkonsistenten Namen gefeit, du hast aber jede menge andere Inkonsistenzen.

Ich schlage mal das hier vor:

Code: Alles auswählen

 item_costs = {'Werkzeug': 20, 'Öl': 4, 'Holzbalken': 6, 'Korb': 3, 'Stoff': 8, 'Kreide': 1, 'Seil': 4, 'Holzeimer':7, 'Harke': 5, 'Axt': 8, 'Lederbeutel': 3}

class Item(object):
    def __init__(name, cost, has_item=False):
        self.name = name
        self.cost = cost
        self.has_item = has_item

items = [Item(name, cost) for name, cost in item_costs.iteritems()]
Zuletzt geändert von cofi am Mittwoch 16. Mai 2012, 13:40, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Du bindest an `item` nichts, sondern versuchst, einem Element "im" `item`-Objekt etwas zuzuweisen. Das kann aber ja nicht klappen, solange `item` an sich nicht existiert!

Dir fehlt ein `item = list()` o.ä.

Generell ist Dein Code aber... naja, umständlich. Die Anzahl an Elementen in einer Datenstruktur kann man in Python üblicherweise via `len(object)` herausbekommen - wozu das umständlich manuell irgend wo vermerken?

Und wozu gibt es zum einen eine Liste von Objekten und zum anderen eine separate Zuteilung der Kosten an diese Objekte? Du könntest in Deinem Beispiel alleine mit dem Dictionary arbeiten. In den Keys stehen doch alle Itemnamen bereits.

Was soll das `hasitem` in Deiner Klasse?

Evtl. beschreibst Du uns mal, was Du eigentlich modellieren willst. Evtl. kann man da eine sinnvollere Datenstruktur finden.

Edit: Ok, deets war schneller ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Milkymalk
User
Beiträge: 12
Registriert: Freitag 11. Mai 2012, 22:22

Ui, danke, so viel Hilfe auf einmal :D

Den Fehler habe ich jetzt verstanden - die Zuordnung erstellt also eine komplette Liste und iteriert nicht bloß alle Elemente, die ich iterativ zuordnen muß.

Wegen der umständlichen Programmierung: Ich glaube, ich bin noch zu sehr von unflexiblen Sprachen wie Basic (C64!) und Turbo Pascal vorbelastet, wo 'len()' z.B. noch etwas ganz anderes gemacht hat und generell so praktische Datenstrukturen meines Wissens nicht vorhanden waren. Das ganze Konzept eines Dictionaries ist für mich schon revolutionär... :roll:

Das Ganze soll am Ende ein Inventory- und Shopsystem werden. Die Variable 'hasitem' soll einfach anzeigen, ob der Spieler das Item besitzt. Ich glaube zwar, daß ich das mit einer separaten Inventoryliste und einer Prüfung mit 'in' eleganter lösen kann, aber da habe ich mich noch nicht herangetraut.

Code: Alles auswählen

item_names = item_costs.keys()
Genau sowas habe ich gebraucht :-) Danke!

Aber apropos Inkonsistenzen - was ist denn noch inkonsistent an dem, was ich geschrieben habe?

@cofi:
Sollte es nicht heißen:

Code: Alles auswählen

class Item(object):
    def __init__(self, name, cost, has_item=False):
        self.name = name
        self.cost = cost
        self.has_item = has_item
Also mit 'self' im '__init__'? Oder wird das von '(object)' übernommen?
Zuletzt geändert von Milkymalk am Mittwoch 16. Mai 2012, 14:07, insgesamt 1-mal geändert.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Milkymalk hat geschrieben: Das Ganze soll am Ende ein Inventory- und Shopsystem werden. Die Variable 'hasitem' soll einfach anzeigen, ob der Spieler das Item besitzt.
Du hast das wohl nicht ganz zu Ende gedacht... ein `ItemInfo`-Objekt beinhaltet die Infos zu *einem* Item. Wenn ein Spieler dieses nicht besitzt, wieso willst Du ihm das Item überhaupt zuordnen?
Milkymalk hat geschrieben: Ich glaube zwar, daß ich das mit einer separaten Inventoryliste und einer Prüfung mit 'in' eleganter lösen kann, aber da habe ich mich noch nicht herangetraut.
Hehe... das wäre wohl der bessere Weg ;-)

Du musst doch in einer Spieler-Klasse ein Attribut haben, welches eine Liste von Items enthält. In diese Liste packst Du eben nur die Items, die ein Spieler auch besitzt...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Milkymalk
User
Beiträge: 12
Registriert: Freitag 11. Mai 2012, 22:22

Hyperion hat geschrieben:Du hast das wohl nicht ganz zu Ende gedacht... ein `ItemInfo`-Objekt beinhaltet die Infos zu *einem* Item. Wenn ein Spieler dieses nicht besitzt, wieso willst Du ihm das Item überhaupt zuordnen?
Der Gedanke war, eine definite Liste aller möglichen Items zu machen und dann einfach eine Variable zu setzen, ob der Spieler dieses Item hat (jedes Item kann man nur einmal haben). Die gleiche Liste könnte ich dann auch für den Shop benutzen.
Du musst doch in einer Spieler-Klasse ein Attribut haben, welches eine Liste von Items enthält. In diese Liste packst Du eben nur die Items, die ein Spieler auch besitzt...
Da es nur einen Spieler gibt fand ich es unnötig, die Liste in die Spielerklasse reinzupacken...
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Milkymalk hat geschrieben:Aber apropos Inkonsistenzen - was ist denn noch inkonsistent an dem, was ich geschrieben habe?
Daran noch nichts, aber da du 3 unabhaengige Strukturen hast, die alle das (bzw Teile davon) selbe Objekt beschreiben (len(item_costs), item_costs.keys() und eben item_costs) sind inkonsistenzen vorprogramiert .. zum Beispiel wenn dein Spieler ein Item findet oder du sonstwie ein neues erstellst.
Milkymalk hat geschrieben:@cofi:
Sollte es nicht heißen:
...
Also mit 'self' im '__init__'? Oder wird das von '(object)' übernommen?
Nein von object wird nichts uebernommen, du ueberschreibst ja gerade dessen `__init__`. Das war nur der Fehler zum selbersuchen, den gibts in jedem 7. Post ;)

Auch wenn es nur einen Spieler gibt macht sich eine Inventar Klasse vielleicht ganz gut, aber du kennst dein Vorhaben am besten. Was du aber nicht machen solltest: Mehrere Strukturen verwenden deren Werte sich voneinander ableiten (und ohne sicherzustellen, dass sich keines aendert, wenn die anderen es nicht auch tun!).
deets

Das ist die falsche Denke. Im allgemeinen gibt es von etwas mehrere Dinge - das du nur *eines* erlaubst, ist ein Spezialfall. Ausserdem will man ja schnell wissen, was ein Spieler hat - stell dir vor du hast 1000.000 Objekte, aber ein Inventory von nur 10 Items. Wieso willst du ueber 1000.000 Items iterieren um die 10 rauszufinden, die der Spieler besitzt?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Tja, das wussten wir ja bisher nicht. Du hast also immer *einen* Spieler und *einen* Shop? Wenn dem so ist, kann man das sicherlich so machen. Ich finde es irgend wie dennoch nicht so prickelnd, wenn Du Infos, die den Spieler betreffen (hat er ein Item?), in die Item-Liste packst, die sich so ja auch direkt im "Shop" befindet.

Wenn man schnell und einfach im Shop nur Sachen anbieten will, die der Spieler noch nicht hat, so kann man da schön mit Sets arbeiten:

Code: Alles auswählen

In [4]: player_items = ["Öl", "Kreide", "Seil"]

In [5]: set(item_names) - set(player_items)
Out[5]: 
set(['Harke',
     'Axt',
     'Holzeimer',
     'Holzbalken',
     'Korb',
     'Werkzeug',
     'Stoff',
     'Lederbeutel'])
Man kann also eine für den Spieler relevante Liste recht einfach berechnen...

Zudem kann man die Liste der Objekte im Shop ja auch nach jedem Verkauf anpassen, also das verkaufte Item einfach aus der Liste entfernen... dann umgehst Du direkt die Problematik, dem Spieler Dinge mehrfach anzubieten.

Wie Du siehst, gibt es meistens viele Alternativen :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Milkymalk
User
Beiträge: 12
Registriert: Freitag 11. Mai 2012, 22:22

@deets:
Wären es so viele würde ich das sicherlich so machen ;-) So wie es aussieht wird sich die Anzahl eher auf 30 beschränken, und die paar Mikrosekunden für die Iteration hat man denke ich ;) Aber ich verstehe, was du meinst.

@Hyperion:
Oha, da blicke ich jetzt gar nicht durch. Mit Sets habe ich mich noch nie beschäftigt, und gerade das 'In[4]' usw. verwirrt mich gerade. Da muß ich mich wohl nochmal ordentlich durchlesen.
Auf jeden Fall danke für die Hilfestellung!
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Besagtes In[4] stammt von IPython, einer verbesserten interaktiven Python-Shell.
Milkymalk
User
Beiträge: 12
Registriert: Freitag 11. Mai 2012, 22:22

Da ich Ren'Py benutze bin ich nicht sicher, ob ich damit etwas anfangen kann.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Milkymalk hat geschrieben:Da ich Ren'Py benutze bin ich nicht sicher, ob ich damit etwas anfangen kann.
Unterstützt Ren'Py keine `sets`?

So, mal mein zweiter Vorschlag ohne sets: Code
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Hyperion: Ren'py liefert Python 2.6 mit aus, also gibt es `set`\s.
Milkymalk
User
Beiträge: 12
Registriert: Freitag 11. Mai 2012, 22:22

Das schon, aber ob ich IPython damit nutzen kann, weiß ich nicht - zudem ich dessen In[4] oder auch ohne Zahl bzw. Out[5] nicht dokumentiert finde.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Milkymalk hat geschrieben:Das schon, aber ob ich IPython damit nutzen kann, weiß ich nicht - zudem ich dessen In[4] oder auch ohne Zahl bzw. Out[5] nicht dokumentiert finde.
Ach so... das hat doch nichts mit Python an sich zu tun ;-) Die IN/OUT[zahl]-Syntax ist einfach nur der Prompt von IPython.

Standard Python-Shell:

Code: Alles auswählen

>>> print("Hallo")
Hallo
IPython-Shell:

Code: Alles auswählen

In [1]: print("Hallo")
Hallo
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Milkymalk
User
Beiträge: 12
Registriert: Freitag 11. Mai 2012, 22:22

Achso :idea:

Da ich eh nur einen Spieler und nur einen Shop habe brauche ich es gar nicht so komplex, wie es hier: https://gist.github.com/2710568 gezeigt wurde - aber es war eine gute Anschauungsübung, die mir irgendwann sicher weiterhilft.

Würde das hier so funktionieren?

Code: Alles auswählen

item_costs = {'Werkzeug': 20, 'Öl': 4, 'Holzbalken': 6, 'Korb': 3, 'Stoff': 8, 'Kreide': 1, 'Seil': 7, 'Holzeimer': 4, 'Harke': 5, 'Axt': 8, 'Lederbeutel': 3}

class Item(object):
    def __init__(self, name, cost):
        self.name = name
        self.cost = cost
        
def has_item(item):
    if item in player_items:
        return True
    else:
        return False
        
def give_item(name):
    player_items.add(shop_items.pop(name)) 

def use_item(name):
    used_items.add(player_items.pop(name)) 

shop_items = set(Item(name, cost) for name, cost in item_costs.iteritems())
player_items = set()
used_items = set()
Zuerst sind alle Items im Shop. Mit 'give_item(name)' gebe ich ein Item dem Spieler, und mit 'use_item(name)' nehme ich es ihm wieder weg und lege es für zukünftige Referenz in der Liste bzw. im Set der benutzten Items ab.
Nein, ich sehe gerade, daß das nicht klappt, weil die Sets ja keine Dictionaries mehr sind, richtig? Wie adressiere ich denn Objekte in Sets?

Oder sollte ich es lieber gleich als Dictionary lassen?

Code: Alles auswählen

item_costs = {'Werkzeug': 20, 'Öl': 4, 'Holzbalken': 6, 'Korb': 3, 'Stoff': 8, 'Kreide': 1, 'Seil': 7, 'Holzeimer': 4, 'Harke': 5, 'Axt': 8, 'Lederbeutel': 3}

def has_item(name):
    if name in player_items:
        return True
    else:
        return False
        
def give_item(name):
    player_items.add(shop_items.pop(name)) 

def use_item(name):
    used_items.add(player_items.pop(name)) 

shop_items = item_costs
player_items = {}
used_items = {}
Bei dieser Methode würde ich aber Probleme bekommen, wenn Items mehr Attribute hätten als nur den Preis; kann man das dann überhaupt mit Dictionaries machen?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Wenn der Name eines `Item`s das Attribut ist, über das Du ein `Item` klassifizierst, dann bietet sich ein Dictionary an:

Code: Alles auswählen

ITEMS = {
    'Werkzeug': 20,
    'Öl': 4,
    'Holzbalken': 6,
    'Korb': 3,
    'Stoff': 8,
    'Kreide': 1,
    'Seil': 4,
    'Holzeimer':7,
    'Harke': 5,
    'Axt': 8,
    'Lederbeutel': 3
}


class Item:

    def __init__(self, name, cost):
        self.name = name
        self.cost = cost

    def __str__(self):
        return "{} ({})".format(self.name, self.cost)


# Dict-Comprehension erst ab Python 3.x
# Du musst es in etwa so umsetzen: shop = dict((name, ...) for ...)
shop = {name: Item(name, cost) for name, cost in ITEMS.items()}

print(shop["Axt"])
Du kannst dann für alle Attribute, die Items aufnehmen sollen wieder ein Dict nehmen, also etwa als Attribut von einer Player-Klasse oder eben einer `used`-Kollektion.

Das hier

Code: Alles auswählen

def has_item(item):
    if item in player_items:
        return True
    else:
        return False
ist extrem umständlich. `in` liefert bereits einen Wahrheitswert:
Python Doku hat geschrieben: Comparisons yield boolean values: True or False.
Also z.B.

Code: Alles auswählen

In [21]: "Axt" in shop
Out[21]: True
Du kannst doch einfach diesen zurückgeben:

Code: Alles auswählen

def has_item(item):
    return item in player_items
Wobei man sich fragt, ob man das überhaupt als Funktion bräuchte oder wieso es keine Methode der Player-Klasse ist.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Milkymalk: Ich weiss nicht so recht in wie weit in einer Ren'py-Umgebung saubere Programmierung eine Rolle spielt, aber normalerweise sollten Funktion nicht auf Daten operieren die nicht als Argument übergeben wurden, ausser es handelt sich dabei um Konstanten.

Dein Code erster Quelltext würde nicht funktionieren, beziehungsweise stimmen die Namen nicht, weil in dem `set()` keine Namen sondern `Item`\s sind. Man muss also bei `give_item()` und `use_item()` ein `Item` übergeben und nicht wie `name` suggeriert eine Zeichenkette mit dem Namen.

Da dort nichts Ren'py-spezifisches enthalten ist, könntest Du solche Sachen auch einfach mit einem normalen Python 2.x ausprobieren. Wenn Du Ren'py auf Code-Ebene erweitern möchtest, solltest Du IMHO sowieso erst einmal die üblichen Python-Grundlagen lernen. Also zum Beispiel das Tutorial aus der Python-Dokumentation durcharbeiten.

Bei Werten kennen Wörterbücher keine Beschränkungen. Das können beliebige Objekte sein. Statt Namen auf Zahlen abzubilden könntest Du auch Namen auf `Item`-Exemplare abbilden.
Antworten