string indices must be integers - Dict - Daten zuweisen

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
slartibartfas
User
Beiträge: 6
Registriert: Donnerstag 29. August 2013, 22:02

Hehey Leute

Ich schreibe grad an einem Parser für die WoW Log File.
Jedoch habe ich ein Problem bei der Erstellung eines Dict und der Zuweisung von daten an jenes.

Kurzerhand habe ich hier:

Code: Alles auswählen

import yaml
 
RAID = {
    "Players" : { "Player"             : "",
                  "Casts" : { "Spell"  : [] },
                  "total_damage_done"  : 0,
                  "total_damage_taken" : 0,
     } 
}
 
 
def main():
 
    R1 = RAID["Players"]["Player"] = "Kerze"
 
    R1["Players"]["Casts"]["Spell"].append(("Feuerball", "2345"))
    R1["Players"]["total_damage_done"] = 234
    R1["Players"]["total_damage_taken"] = 24
 
 
    print(yaml.dump(R1, default_flow_style=False))
 
if __name__ == '__main__':
    main()
Das Problem isoliert, so das es sehr übersichtlich ist und nicht den teil des ganzen Codes hier gepostet.
Der Error ist anscheinend in der Zeile:

Code: Alles auswählen

R1["Players"]["Casts"]["Spell"].append(("Feuerball", "2345")) 
und lautet:

Code: Alles auswählen

TypeError:  string indices must be integers
Mein Problem liegt eher in der Natur des Verstehens dieses Fehlers.
Ich hoffe jemand kann mit beschreiben, was genau der Fehler ist.

Ich grüsse Euch.
Zuletzt geändert von slartibartfas am Montag 26. Oktober 2020, 13:10, insgesamt 3-mal geändert.
Benutzeravatar
sparrow
User
Beiträge: 4237
Registriert: Freitag 17. April 2009, 10:28

Du kannst Code hier zwischen Code-Tags posten.
Die erscheinen automatische, wenn du im "vollständiger Editor & Vorschau" den </>-Button drückst.

Und wie sollen wir dir denn erklären, was der Fehler ist, wenn du uns nicht sagst, was eigentlich nicht geht?
Gibt es Fehlermeldungen?
Wo tut der Code etwas anderes als du erwartest?
slartibartfas
User
Beiträge: 6
Registriert: Donnerstag 29. August 2013, 22:02

Entschuldigung, bin auf der Arbeit und habe nicht an alles gedacht. Beitrag wurde editiert.
Sirius3
User
Beiträge: 17830
Registriert: Sonntag 21. Oktober 2012, 17:20

R1 hat den Wert "Katze", und warum sollte ein String einen Schlüssel "Players" haben?
Es ist sehr schlecht, dass Du eine Konstante veränderst. Das verwirrt noch mehr, als dass Du globale Variablen verwendest.
R1 sollte einen besseren Namen haben, damit man wenigstens raten kann, was Du mit dem Code ausdrücken wolltest.
slartibartfas
User
Beiträge: 6
Registriert: Donnerstag 29. August 2013, 22:02

Es ist ein Kampflog, da sind mehrere Spieler und es ist ein Dict wo alle Spieler mit Ihren jeweiligen daten gespeichert werden um nachher nach Kriterien ausgeben zu lassen.
RAID sieht aus wie eine Konstante, sollte es aber nicht sein. Mea Culpa
"Kerze" ist also ein Player, der unter Players zu finden ist.

Z.b:

Code: Alles auswählen

print("Gesammter Schaden gemcht von Kerze: {}".format(R1["Players"]["Kerze"]["total_damage_done"]))
Benutzeravatar
sparrow
User
Beiträge: 4237
Registriert: Freitag 17. April 2009, 10:28

Was denkst du denn, was diese Zeile tut?:

Code: Alles auswählen

R1 = RAID["Players"]["Player"] = "Kerze"
Dem Eintrag ["Players"]["Player"] in RAID wird der Wert "Zeichenkette" zugewiesen.
Der Wert von RAID["Players"]["Player"] wird an den Namen "R1" gebunden. Und der Wert ist "Kerze".
slartibartfas
User
Beiträge: 6
Registriert: Donnerstag 29. August 2013, 22:02

Es tut mir so leid, ich hab mich jetzt nochmals dran gemacht.
Alleine das: x = g = c... hätte mir schon klar sein müsse; Das geht so nicht, da stimmt was nicht.

Naja jetzt hab ich in aller ruhe nochmals das Hirn eingeschalten und es läuft wie muss:

Code: Alles auswählen

from   random import randint
import yaml


Raid = { }


def main():

    member = "Diso"

    for i in range(0, 10):

        d = randint(0, 42)

        if member not in Raid:
            print("NOT IN LIST")
            Raid[member] = { 'Name': member,
                'Cast': "Feuerball",
                'total_dmg_done' : d
            }

        elif member in Raid:
            print("IN LIST: DAMAGE = {}".format(d))
            Raid[member]['total_dmg_done'] += d
    
    print(yaml.dump(Raid, default_flow_style=False))



if __name__ == '__main__':
    main()
Benutzeravatar
Trägheit
User
Beiträge: 12
Registriert: Samstag 24. Oktober 2020, 20:14

Grüße!

Meine Zeit in WoW ist schon eine ganze Weile her... :roll: Ich weiß nicht mehr genau wie dort die Logs aussahen. Ich meine mich jedoch daran zu erinnern, das man da einiges an Vorverarbeitung (parsen) reinstecken musste, um mit den Daten was verarbeitbares anfangen zu können.

-> Ich gehe hier mal von einem einfachen Format aus, könnte im Prinzip CSV sein.

Der Code ist mit Vorsicht zu genießen. Ich bin selber noch am Dazulernen. Es gibt mit Sicherheit pythonschere :wink: Wege das anzugehen.
Vielleicht kann ja einer der erfahrenen :geek: mal darüber schauen.

Code: Alles auswählen

from collections import namedtuple
from recordtype import recordtype

def Player(LogFileRecord):
    return recordtype("Player", ["name", "class_name", "total_damage_done", "spell_log"])(
                name = LogFileRecord.player_name,
                class_name = LogFileRecord.class_name,
                total_damage_done = 0, 
                spell_log = {}
                )

def Spell(LogFileRecord):
    return recordtype("Spell", ["name", "damage_total"])(
        name = LogFileRecord.class_name,
        damage_total = LogFileRecord.spell_damage
        )

def main():
    
    LogFileRecord = namedtuple("LogFileRecord", ["player_name", "class_name", "spell_name", "spell_damage"])

    log_content = [
        ["Asmongold", "Krieger", "Hinrichten", 120],
        ["Mcconnell", "Paladin", "Gottesschild", 0],
        ["Thuganomics", "Magier", "Frostblitz", 75],
        ["Asmongold", "Krieger", "Hinrichten", 240],
        ["Thuganomics", "Magier", "Frostblitz", 135],
        ["BaldKing", "Todesritter", "Auslöschen", 150],
        ["Thuganomics", "Magier", "Eislanze", 80],
        ["Mcconnell", "Paladin", "Ruhestein", 0]
    ]
    
    log_file_records = map(LogFileRecord._make, log_content)
    
    Raid = namedtuple("Raid", ["players"])(
                players = {}
                )

    for record in log_file_records:
        if record.player_name not in Raid.players:
            Raid.players[record.player_name] = Player(record)

        player = Raid.players[record.player_name]
        spell_log = player.spell_log
        if record.spell_name not in spell_log:
            spell_log[record.spell_name] = Spell(record)
        else:
            spell_log[record.spell_name].damage_total += record.spell_damage

        player.total_damage_done += record.spell_damage
    
    for _ , player in Raid.players.items():
        print(f"Spieler '{player.name}' [{player.class_name}] hat insgesamt {player.total_damage_done} Schaden ausgeteilt.")


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17830
Registriert: Sonntag 21. Oktober 2012, 17:20

@Trägheit: Variablennamen schreibt man komplett klein, das ist vor allem bei LogFileRecord sehr verwirrend, da es auch die Klasse LogFileRecord gibt.
Klassen werden nicht innerhalb von Funktionen definiert.
Ein Namedtuple, das nur aus einem Element besteht, ist etwas komisch.
Benutzeravatar
Trägheit
User
Beiträge: 12
Registriert: Samstag 24. Oktober 2020, 20:14

'namedtuple' erstellt doch eine Klasse, oder nicht? Darum hab ichs groß geschrieben. :_)

edit: Wenn, dann ist es sogar in der Python docu falsch - was mich nicht wundern würde, weil in den Beispielen auch noch Python2 kram steht (z.B. bei der String-Formatierung). ~gnaa
Warum muss das nur so verwirrend sein. :cry:

edit2:
Sirius3 hat geschrieben: Dienstag 27. Oktober 2020, 09:42 Ein Namedtuple, das nur aus einem Element besteht, ist etwas komisch.
Versteh dich. Ist nur in dem Beispiel so. Theoretisch kommt da noch mehr hinein (z.B. besteht ein Raid aus Gruppen; im Raid gibt es unterschiedliche Rollenverteiliungen, usw. usf.). ;)
Benutzeravatar
Trägheit
User
Beiträge: 12
Registriert: Samstag 24. Oktober 2020, 20:14

@Sirius3:
Ich glaub ich steige gerade hinter deinen Satz: "Klassen werden nicht innerhalb von Funktionen definiert"
Im Prinzip erstelle ich in der Funktion ja jedesmal die Klasse neu (neben dem Initialisieren); Dabei reicht es aus - und macht auch mehr Sinn - diese ein einziges mal zu erzeugen und dann kann ich daraus beliebig viele Objekte erstellen. Das hast du gemeint, oder?
Benutzeravatar
Trägheit
User
Beiträge: 12
Registriert: Samstag 24. Oktober 2020, 20:14

Wäre das nun üblich so zu schreiben?

Code: Alles auswählen

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])

p1 = Point(1, 1)
print(p1, type(p1), id(p1))

p2 = Point(-3, 7)
print(p2, type(p2), id(p2))
'Point' ist die erstellte Klasse, und weil der Bezeichner verdeutlichen soll, dass es eine Klasse ist, ist es üblich den so zu schreiben? Das würde dann zumindest Sinn ergeben, weil ich gelesen habe, dass es üblich ist Klassen-Namen groß zu schreiben (z.B. 'class Point' oder 'class DeviceAdapter').
Sirius3
User
Beiträge: 17830
Registriert: Sonntag 21. Oktober 2012, 17:20

Genau, Klassen schreibt man gross.
Benutzeravatar
sparrow
User
Beiträge: 4237
Registriert: Freitag 17. April 2009, 10:28

@Trägheit: Nur für den Fall, dass es dich verwirrt:

Das hier ist nicht, was Sirius3 meinte:

Code: Alles auswählen

LogFileRecord = namedtuple("LogFileRecord",...
Sondern hier ist die Benennung verwirrend:

Code: Alles auswählen

def Player(LogFileRecord):
In der Funktion "Player" gibt es nun eine lokale Variable LogFileRecord, die ihrer Schreibweise nach der Name einer Klasse ist.
Du möchtest aber eine Instanz der Klasse als Parameter. Also eher "logfilerecord" oder "record".

Auch die Namen von Funktionen schreibt man normalerweise klein. Und in der Regel sollen sie die Tätigkeit beschreiben, die sie ausführen.
Benutzeravatar
__blackjack__
User
Beiträge: 13242
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Trägheit: `recordtype` wird vom Autor nicht mehr weiterentwickelt, der verweist auf sein Projekt `namedlist`, und dort steht dann als erstes die Warnung, dass das nicht mehr gewartet wird.

Ich würde für so etwas `attrs` verwenden. Das wird in mehreren bekannten Projekten verwendet und aktiv entwickelt. Und man kann es auch gleich anstelle von `namedtuple` verwenden.
Please call it what it is: copyright infringement, not piracy. Piracy takes place in international waters, and involves one or more of theft, murder, rape and kidnapping. Making an unauthorized copy of a piece of software is not piracy, it is an infringement of a government-granted monopoly.
Antworten