csv-Datei auslesen -> Teile herausfiltern -> Problem

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
katze_sonne
User
Beiträge: 3
Registriert: Mittwoch 29. Dezember 2010, 14:30

Hallo!

ich bin noch ein ziemlicher Neuling was Python angeht und bin nun auf ein Problem gestoßen, an den ich mir seit ein paar Stunden die Zähne ausbeiße - und langsam keine Idee mehr habe...

Vorweg sei gesagt: Das "Programm" ist in Python 3 programmiert.

Kleine Beschreibung des Programms: Ein Vokabeltrainer, der seine Vokabeln aus Dateien im CSV-Format importiert (genauer gesagt welche, die wie mit dem dict.cc-Vokabeltrainer exportierten Dateien formatiert sind). Hier mal ein Beispiel (die einzelnen Spalten sind per Tab abgetrennt, eckige Klammern bedeuten Kommentare und geschweifte Klammern bestimmen den Genus -> Geschlecht):

Code: Alles auswählen

vorangegangen [bla]	preceding
wegziehen	to pull away
Fahnenmast {m}	flagpole
Vorrat {m}	stock [supply]
Kiefer {f} [Baum]	pine tree
Eingelesen wird die Datei per:

Code: Alles auswählen

import sys
import csv

#Zugriffsversuch und Einlesen aller Vokabeln
try:
    Vokabelliste = csv.reader(open("vok.csv"), delimiter="\t")
except:
    print("Fehler bei Dateizugriff!")
    sys.exit(0)
Der weitere Code ist folgender:
http://www.python-forum.de/pastebin.php?mode=view&s=113 Nicht mehr aktuell! -> aktueller Link siehe unten oder hier

("zeile[0]" bezeichnet jeweils die Deutsche, "zeile[1]" jeweils die Englische Vokabel...)

Nun zu meinem Problem: Die auskommentierte Zeile 44 macht im übrigen genau das was sie soll: Alle Vokabeln und ihr Gegenstück ausgeben.

Meines Wissens nach sollten die Zeilen 52 und 53 genau das gleiche machen (zumindest hatte ich das so vor). Allerdings wird hier leider gar nichts ausgegeben, im Debugger wird die Zeile 53 niemals aufgerufen :-(

Das ist genau der Punkt an dem ich bisher gescheitert bin, ich finde einfach den Fehler nicht - ich würde mich freuen, wenn mir jemand helfen könnte :-)

Gruß,
katze_sonne

EDIT: In eine Funktion habe ich das gepackt, weil ich das ganze auch nochmal für die geschweiften Klammern mit dem Genus machen wollte... - ohne hat das Programm schon mal funktioniert ^^ - aber sieht halt unschön aus, zweimal den fast gleichen Programmtext niederzuschreiben...

EDIT2: So, ich habe das Programm nochmals ein wenig überarbeitet / vereinfacht (dabei hat mir aber jemand anderes geholfen - der hat den Fehler allerdings auch nicht gewusst)... Das Programm läuft (oder läuft nicht) wie gehabt - ist jetzt aber hoffentlich etwas besser verständlich...:
Siehe: http://www.python-forum.de/pastebin.php?mode=view&s=114
BlackJack

@katze_sonne: Der Verständnisfehler ist beim Kommentar über dem Aufruf von `csv.reader()`: Das liesst nicht alle Vokabeln ein, sondern erstellt ein "Reader"-Objekt das beim iterieren erst die Datei Datensatz für Datensatz liest. Und wenn man über alle Datensätze iteriert hat, dann ist der Dateizeiger am Ende der Datei angelangt und man kann da nicht so einfach noch einmal drüber iterieren. Du solltest das Programm so schreiben, dass alles in einem durchgang erledigt wird und/oder die Datensätze in einer Datenstruktur abgelegt werden über die man mehrfach iterieren kann.

Der Name `Vokalliste` für das "Reader"-Objekt ist in dem Zusammenhang auch irreführend, weil es eben keine Liste ist. Selbst wenn es eine Liste wäre, sollte der Typ besser nicht so direkt im Namen stehen, denn dann kann man den Typ nicht ändern ohne dass man auch den Namen ändern muss. Für Exemplare von Containerobjekten bietet sich die Mehrzahl bei der Namensgebung an, also in diesem Fall `vokabeln`. Kleingeschrieben, weil das den Namenskonventionen aus dem Python Style Guide entspricht.

Sonstige Manöverkritik: Man sollte kein reines ``except`` ohne konkrete Ausnahmen verwenden. Damit kann man Ausnahmen "verschlucken", was die Fehlersuche enorm erschweren kann. Wenn Du in der Zeile zum Beispiel einen Namen falsch geschrieben hättest, dann würde es einen `NameError` geben, den Du nie zu Gesicht bekommst, der auch zur "Fehler bei Dateizugriff!"-Ausgabe führt.

Dateien die man öffnet, sollte man auch explizit wieder schliessen. Schau Dir dazu mal die ``with``-Anweisung an.

Funktionen sollten nicht auf Daten zugreifen die nicht als Argumente übergeben wurden. (Ausser es handelt sich um Konstanten). Das betrifft `Vokabelliste` in `herausfiltern()`.

Für den Kommentar am Anfang der Funktion würde sich ein Docstring eignen, damit man da auch im Interpreter oder über Werkzeuge zum erstellen von API-Dokumentation heran kommt.

Der Kommentar zu `AusDict` ist mangelhaft. "Ausgabe-Dictionary" sagt fast genau so wenig wie der Name selbst und wenn man sich die Datenstruktur anschaut, dann stimmt das noch nicht mal, weil es zwei Dictionaries in einer Liste sind. Kommentare sollten auch nicht das Offensichtliche noch einmal beschreiben, sondern die Bedeutung von Code, die aus dem Code selber nicht schon ersichtlich ist. Dort als Kommentar dran zu schreiben, dass es sich um eine Liste mit zwei Dictionaries drin handelt, wäre auch nicht besser.

Die Kommentare nach ``if zeile`` verstehe ich alle nicht wirklich. Und der Quelltext ist auch sehr verwirrend mit der ganzen Indexerei mit den 0en und 1en. Da sollte man die Elemente an verständliche(re) Namen binden.

Ausserdem scheint mir, dass Du eigentlich zusammengehörige Daten auf mehrere Datenstrukturen verteilst!?
katze_sonne
User
Beiträge: 3
Registriert: Mittwoch 29. Dezember 2010, 14:30

Hallo BlackJack!

Vielen Dank für deine ausführliche Antwort :D

Zu der Antwort auf meine eigentliche Frage: Ok, dann ist natürlich klar :wink: - das hab' ich ja total falsch verstanden gehabt... Danke für das aufklären des Missverständnisses!

Zu der Kritik: Auch hierfür danke :D Ich habe mich mal versucht die entsprechenden Sachen (und auch sonst vieles) zu verbessern - geblieben ist eigentlich kaum noch etwas vom ursprünglichen Programm :K Ich hoffe aber, dass das so (zumindest halbwegs) in Ordnung ist? Wäre nett, wenn du dich hierzu noch mal äußern könntest...

Hier mein aktueller Code:
http://www.python-forum.de/pastebin.php?mode=view&s=115

Gruß,
k_s
BlackJack

@katze_sonne: Die Daten werden immer noch in parallele Datenstrukturen verteilt. Du hast drei Listen, bei denen Einträge am gleichen Index zusammen gehören, statt einer Liste mit Einträgen, die jeweils alle Daten zu einer Vokabel enthalten.

Eine "Zelle" der Tabelle sollte in ein Objekt mit allen Informationen umgesetzt werden. Also 'Kiefer {f} [Baum]' könnte in ein "benanntes" Tupel ``Word(word='Kiefer', comment='Baum', genders='f')`` umgesetzt werden. Dann wären die Informationen, die zu diesem Wort gehören, als *ein* Objekt im Programm vorhanden. Wenn man die Übersetzungen des Wortes noch in einem Objekt zusammenfasst und solche Objekte in eine Liste steckt, kann man die zum Beispiel ganz einfach mit `random.shuffle()` für's Vokabeln lernen "mischen", ohne dass man die zusammengehörigen Informationen "auseinander reisst".

Wenn man bei `namedtuple` bleibt, kann man eine Funktion schreiben, die eine Zeichenkette mit dem Inhalt einer "Zelle" aus der Datei bekommt, und das entsprechende Tupel-Objekt zurück gibt, und eine Funktion, die daraus wieder eine gleichwertige Zeichenkette erstellt. Der nächste Schritt wäre dann eine eigene Klasse zu schreiben, mit einer `classmethod()` zum Zerlegen einer Zeichenkette und einer entsprechenden `__str__()`-Implementierung für die Darstellung eines Wortes als Zeichenkette.
katze_sonne
User
Beiträge: 3
Registriert: Mittwoch 29. Dezember 2010, 14:30

Danke für deine Hilfe noch mal :D

Das mit der Zelle habe ich jetzt mal so umgesetzt wie ich verstanden habe wie du es vorgeschlagen hast ;)

Außerdem habe ich das random.shuffle() jetzt benutzt - ist ja auch ne nette Funktion :-)

Das Ergebnis (nur der veränderte Teil + der Teil der zur Abfrage ist): http://www.python-forum.de/pastebin.php?mode=view&s=116

Das mit der extra Funktion, die jedes Mal einen Zugriff auf die Datei macht könnte man machen, klar, aber würde das das Programm nicht evt. verlangsamen (besonders, wenn im Hintergrund z.B. eine Kopierfunktion läuft)? So wie jetzt hat es halt den Vorteil, Speicher zu vereinnahmen, aber ich denke kaum, dass die paar Wörter in diesem Fall zu viel Speicher fressen... (habe das mal mit einer Liste mit insgesamt 1375 Wörtern getestet und der in der Systemüberwachung angezeigt Speicherbedarf des Python3 Prozesses ist gerade mal um 2,6 MB gestiegen - ich denke also nicht, dass das was bringt - oder gibt es da noch andere Argumente?)

Das mit der Klasse / classmethod() ist soweit ich mir darunter was vorstellen kann eine gute Idee, allerdings bin ich bisher noch nicht soweit, so etwas schreiben zu können - ich behalte das aber mal im Hinterkopf :idea:

Gruß,
k_s
BlackJack

@katze_sonne: Die Funktion soll nicht jedes mal die CSV-Datei einlesen. Ich meinte es sollte eine Funktion geben, die den Inhalt einer "Zelle" mit einem Aufruf umwandelt. Also man gibt die Zeichenkette 'Kiefer {f} [Baum]' rein und bekommt ``type_vocab(word='Kiefer', comment='Baum', gender='f')`` heraus. Dann kannst Du diese Funktion in jedem Datensatz auf jede "Zelle" anwenden.

`vocab_temp` macht den Quelltext übrigens IMHO alles andere als übersichtlicher. Eher unnötig kompliziert. Diese Indexerei mit 0en und 1en ist da auch wieder.

Mit einer Funktion die einen Eintrag als Zeichenkette nimmt und den umwandelt, könnte man `vocabulary` in einer Zeile aufbauen:

Code: Alles auswählen

vocabulary = [[str2vocable(w) for w in row] for row in rows]
Bei der Abfrage würde ich den Quelltext ohne Index schreiben. Das ist in Python recht ungewöhnlich, weil man über die Datenstrukturen in der Regel direkt, ohne Index iterieren kann. Sollte man zusätzlich eine Zählvariable benötigen, gibt es immer noch die `enumerate()`-Funktion.
Antworten