Klasse zum auslesen einer Datei

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.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Hallo an alle,
ich komme leider wieder nicht bei einer Aufgabe weiter :oops:
Wir sollen mittels einer Klasse eine CSV Datei auslesen und die Werte wieder zurückgeben.

Code: Alles auswählen

import csv

class Table:
    def __init__(self, titles=None, cols=None, filename = None):
        self.titles = titles
        self.cols = cols
        self.filename = filename
    
    def __str__(self):
        return (str(self.titles) +', '+ str(self.cols))
        
    def __eq__(self, other):
        try:
            return (self.titles, self.cols) == (other.titles, other.cols)
        except AttributeError:
            return NotImplemented
        
    def parse_csv(self, filename):
        with open(filename, newline='') as csvfile:
            spamreader = csv.reader(csvfile)
            for row in spamreader:
                return row
"__str__" soll zu Ausgabe der Attribute dienen, "__eq__" vergleicht zwei verschiedene Eingaben, und "parse_csv" bekommt einen Dateinamen übergeben und soll die Zeilenüberschriften und Spalten auslesen und diese ans Objekt zurückgeben.
Wenn ich die Klasse allerdings mit einer Datei aufrufen, springt er direkt in die "__str__" Methode, wie kann man das ändern?

Viele Grüße
Zuletzt geändert von Anonymous am Freitag 27. Januar 2017, 15:07, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Hallo,


ich habe deine Klasse mal in python2.6 genutzt und habe keine Probleme.

CSVReader = Table()
print CSVReader.parse_csv('local.csv')
Und anschließend printet er mir die CSV.

Auch wenn ich die Klasse mit Table('','','local.csv') erstelle, bekomme ich keinen Fehler.

Zeige mal deinen Quellcode, der zu deinem Fehler führt.
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ambriador: die parse_csv-Methode macht so keinen Sinn. Es wird nichts von der Klasse benutzt. Das ist eigentlich eine ganz normale Funktion. Ich würde ja erwarten, dass das eine Klassenmethode ist, die ein neues Table-Objekt zurückliefert. In __eq__ erzeugst Du Tuple, statt and zu verwenden.

Wie sieht denn der Aufruf aus?
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Ein Aufruf wäre z.b.:

Code: Alles auswählen

titles = ["Zeit", "Temperatur"]
my_table3 = Table(titles, [[0.0, 3.0, 6.0, 9.0], [15.2, 16.1, 14.8, 20.2]])
print(my_table3)
my_table3_parsed = Table(filename = "simple_example.csv")
print(my_table3_parsed)
Die Ausgabe sieht dann so aus:

Code: Alles auswählen

['Zeit', 'Temperatur'], [[0.0, 3.0, 6.0, 9.0], [15.2, 16.1, 14.8, 20.2]]
None, None
In der CSV Datei stehen die selben Werte wie bei my_table3 gegeben. Die Formatierung, bzw "aufschreiben", der Ausgabe von "parse_csv" stimmt zwar auch noch nicht, aber ich bekomme mit einer Datei ja noch nicht mal das raus was ich in "parse_csv" geschrieben habe.
Wenn ich "parse_csv" so einzeln aufrufe, bekomme ich auch ein Ergebnis, aber hier in der Klasse springt er mit dem Dateiname direkt zur "__str__" Methode.
Mit dem Aufruf von "__eq__" gabs keine Probleme.
BlackJack

@Ambriador: Innerhalb der Klasse wird nirgendwo hin gesprungen. Die `__str__` wird durch das `print()` in der letzten Zeile des Hauptprogramms aufgerufen. Und da die `__init__()` nichts weiter anstellt mit `filename` als es an ein Attribut zu binden wird ``None, None`` ausgegeben, denn das sind ja die Werte die in der `__init__()` an die Attribute `titles` und `cols` gebunden wird wenn man für die gleichnamigen Argumente nichts übergibt. Wieso erwartest Du das auf magische Weise `parse_csv()` aufgerufen werden sollte? Und selbst wenn das der Fall wäre, dann würde an die beiden Attribute `titles` und `cols` trotzdem nichts anderes gebunden, denn die Methode tut das ja letztendlich nicht.

`__eq__()` hast Du hoffentlich nicht direkt aufgerufen sondern den ``==``-Operator verwendet‽

Dir Schnittstelle von der `__init__()` ist unschön. Ich würde da auch eher eine API wie von Sirius3 beschrieben, erwarten.

Warum nennst Du das Reader-Objekt `spamreader`? `spam`‽
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

spamreader ist der Beispielname im Tutorial...
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Hatte da gar nicht dran gedacht das "__str__" durch das "print()" aufgerufen wird.
Dann bekomme ich den Error für "assert my_table3_parsed == my_table3" auch nur deswegen weil die parse_csv nicht das passende Ergebnis ausgibt.
Das was aus der Datei gelesen wird soll so aussehen:

Code: Alles auswählen

my_table3 = (["Zeit", "Temperatur"], [[0.0, 3.0, 6.0, 9.0], [15.2, 16.1, 14.8, 20.2]])
Bekomme es aber nicht richtig hin die csv passend "auszulesen".
"['Zeit', 'Temperatur']" bekomme ich mit "next(reader)", aber wie schaffe ich es das er die erste Spalte in eine Liste, und die zweite Zeile seperat in eine Liste packt?
BlackJack

@Ambriador: Schleifen und die `zip()`-Funktion (oder `itertools.izip()` in Python 2) oder die `zip()`-Funktion (plus `list()` in Python 3) mit einem ”Trick” beim Aufruf von `zip()` für das transponieren von Zeilen und Spalten.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Hab jetzt endlich was hinbekommen, allerdings arbeitet er "parse_csv" immernoch nicht ab.
Ich muss ja die __init__ ändern so wie ich das verstanden habe, aber wie?
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ambriador: und was hast Du hinbekommen?
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

MIt Hilfe konnte ich die parse_csv so hinbekommen das sie eine richtige Ausgabe hinbekommt, und auch den Aufruf hab ich hinbekommen, aber jetzt bekomme ich den vergleich nicht hin.

Code: Alles auswählen

import csv
class Table:
    def __init__(self, titles=None, cols=None, filename = None):
        if filename is None:
            self.titles = titles
            self.cols = cols
            self.filename = None
        else:
            self.filename = filename
            self.parse_csv(filename)
    
    def __str__(self):
        return (str(self.titles) +', '+ str(self.cols))
        
    def __eq__(self, other):
        if self.filename == None:
            return (self.titles, self.cols) == (other.titles, other.cols)
        else:
            return self.filename == other
                    
    def parse_csv(self, filename=None):
        a=open("simple_example.csv",'r')
        timel,templ,count=[],[],0
        final=[]
        for l in a:
            c=l.split(',')
            if count >=1: 
                timel.append(float(c[0]))
                templ.append(float(c[1]))
                if count==0: #this is just append the header in the fianl list
                    c[1]=c[1][:-1]
                    final.append(c)
                    count+=1  
        final.append([timel,templ])
        final=tuple(final)
        return final
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ambriador: was ist denn Dein Ziel? Denn aus dem Code wird das nicht klar. Wird filename angegeben, wird ein Instanz mit komplett anderer Funktionalität erzeugt, als wenn nicht. parse_csv hat mit der Klasse an sich immer noch nichts zu tun. filename wird nicht verwendet, a, c oder l sind schlechte Variabelnamen, weil sie nichts aussagen. Was soll die 1 bei time1 und temp1? Die for-Schleife macht nichts, weil count nie >= 1 wird. Dass open nicht definiert ist, deutet darauf hin, dass was ziemlich schräges gemacht hast.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Sorry hatte mich auch verschrieben, das mit dem "open" fehler kommt nicht mehr vor.
Ich will/soll den Inhalt einer Datei mit einer gegebenen Liste vergleichen, wobei die Datei genau so aussieht wie die Liste:

Code: Alles auswählen

titles = ["Zeit", "Temperatur"]
filename1 = "simple_example.csv"
my_table3 = Table(titles, [[0.0, 3.0, 6.0, 9.0], [15.2, 16.1, 14.8, 20.2]])
my_table3_parsed = Table(filename = "simple_example.csv")
assert my_table3_parsed == my_table3
Ich verzweifle schon ein paar Tage an der Aufgabe und komme nicht wirklich weiter, deswegen bin ich froh das überhaupt mit parse_csv hinbekommen zu haben.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Sieht leider nicht gut aus und ist auch bestimmt total unübersichtlich aber ich wollte mal schauen was er denn nun ausgibt für die Funktion parse_csv bzw den code der dadrin steht. Da ich leider echt nicht weiter weiss ist hier mein letzter Stand, über jede Hilfe wäre ich echt mega dankbar. Wenn ich den Code aus der parse_csv einzeln benutze und auf eine Datei anwende gibt er die Datei richtig aus:

Code: Alles auswählen

import csv

class Table:
    def __init__(self, titles=None, cols=None, filename = None):
        if filename is None:
            self.titles = titles
            self.cols = cols
            self.filename = None
        else:
            self.filename = filename
            self.parse_csv(filename)
    
    def __str__(self):
        if self.filename == None:
            return (str(self.titles) +', '+ str(self.cols))
        else:
            return str(self.parse_csv(self.filename))
        
    def __eq__(self, other):
        if self.filename == None:
            return (self.titles, self.cols) == (other.titles, other.cols)
        else:
            return self.parse_csv(self.filename) == (other.titles, other.cols)
                    
    def parse_csv(self, filename):
        a=open(filename,'r')
        timel,templ,count=[],[],0
        final=[]
        for l in a:
            c=l.split(',')
            if count >=1: 
                timel.append(float(c[0]))
                templ.append(float(c[1]))
                if count==0: #this is just append the header in the fianl list
                    c[1]=c[1][:-1]
                    final.append(c)
                    count+=1  
        final.append([timel,templ])
        final=tuple(final)
        return final
    
my_table1 = Table(["title1", "title2"], [["l1"], ["l2"]])

print(my_table1)

assert my_table1.titles[0] == "title1"
assert my_table1.titles[1] == "title2"
assert my_table1.cols[0] == ["l1"]
assert my_table1.cols[1] == ["l2"]

my_table2 = Table(["title1", "title2"], [["l1"], ["l2"]])
assert my_table2 == my_table1


titles = ["Zeit", "Temperatur"]
filename1 = "simple_example.csv"
my_table3 = Table(titles, [[0.0, 3.0, 6.0, 9.0], [15.2, 16.1, 14.8, 20.2]])
my_table3_parsed = Table(filename = "simple_example.csv")
print(my_table3)
print(my_table3_parsed)

assert my_table3_parsed == my_table3
Die Ausgabe sieht dann so aus:

Code: Alles auswählen

['title1', 'title2'], [['l1'], ['l2']]
['Zeit', 'Temperatur'], [[0.0, 3.0, 6.0, 9.0], [15.2, 16.1, 14.8, 20.2]]
([[], []],)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-225-53aa68bfdaf6> in <module>()
     60 print(my_table3_parsed)
     61 
---> 62 assert my_table3_parsed == my_table3

AssertionError: 
BlackJack

@Ambriador: Es ist ganz sicher *nicht* gewollt das Du da im Grunde zwei verschiedene Datentypen in `Table` vereinst und in jeder Methode nicht nur immer zwischen beiden mit ``if`` unterschieden wird, sondern auch noch bei jeder Operation die Datei neu eingelesen wird.

Die `parse_csv()`-Methode sollte auch einen Einfluss auf den Zustand des Objekts haben, damit das überhaupt eine Methode ist. `__str__()` und `__eq__()` sollten nur auf `self.titles` und `self.cols` operieren und kein ``if``/``else`` enthalten. Den Methoden sollte es egal sein ob die Werte für die Tabelle beim erzeugen übergeben wurden, oder ob sie aus einer Datei geladen wurden.

Warum verwendest Du das `csv`-Modul nicht mehr? Ausserdem gibt es sicher Punktabzug weil die Methode nur mit zwei Spalten klar kommt. Das soll ganz sicher generischer sein, also mit jeder Tabelle mit Kopfzeile und Zahlen mit beliebig vielen Zeilen und Spalten klar kommen.

Die verschachtelten ``if``\s und der Wert von `count` — das funktioniert so *überhaupt nicht*. Beschreib mal den Programm ablauf bis zu dem ``count+=1`` das erste mal ausgeführt wird!

Werd bei Gelegenheit mal dieses unsinnige `my_` vor den ganzen Namen für die Tabellen los. Was soll das? Das bringt Null Informationsgewinn beim lesen.

`a`, `l`, und `c` sind keine guten Namen. Da muss man jetzt raten was die bedeuten sollen?

Die Datei wird geöffnet, aber nicht wieder geschlossen.

Wenn man `__eq__()` implementiert, dann sollte man unbedingt auch `__neq__()` implementieren. Was ja super simpel ist wenn man `__eq__()` schon hat. Und `__hash__()` müsste man dann auch gleich überschreiben damit die Werte sich nicht ”komisch” verhalten.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Alles ab Zeile 40 ist so vorgegeben, da habe ich leider keinen Einfluss drauf, deswegen werde ich die Punkte übergehen. Desweiteren sollen wir keine weiteren Methoden einbauen bis auf die die gegeben sind.
Aber wenn ich z.b. ein "assert my_table3_parsed == my_table3" aufrufe, dann springt er ja automatisch in die "__eq__" Methode, wenn ich da dann nur eine Bearbeitung für die Attribute drin habe bekomme ich ja nen False oder nicht?
Das csv Modul benutze ich nicht mehr weil ich damit nie zu einer Lösung gekommen bin, das ist das einzige was läuft. Wie gesagt, einzeln bekomme ich das passende Ergebnis:

Code: Alles auswählen

a=open("simple_example.csv",'r')
timel,templ,count=[],[],0
final=[]
for l in a:
    c=l.split(',')
    if count >=1: 
        timel.append(float(c[0]))
        templ.append(float(c[1]))
    if count==0: #this is just append the header in the fianl list
        c[1]=c[1][:-1]
        final.append(c)
        count+=1  

final.append([timel,templ])
final=tuple(final)

print(final)

Code: Alles auswählen

(['Zeit', 'Temperatur'], [[0.0, 3.0, 6.0, 9.0], [15.2, 16.1, 14.8, 20.2]])
BlackJack

@Ambriador: Du bekommst beim `__eq__()` nur ein `False` wenn die Attribute nicht gleich sind. Das sollten sie aber sein, falls nicht hast Du etwas falsch gemacht.

Einzeln hast Du aber auch etwas anderes als in der `read_csv()`-Methode denn dort ist der Programmablauf anders. Das ``count+=1`` kann dort niemals erreicht werden. Deswegen fragte ich ja nach einer Beschreibung wie Du denkst das das ausgeführt werden sollte.

Dass es so geht, aber mit dem `csv`-Modul nicht, kann nicht sein. Denn letztendlich machst Du die Arbeit des `csv`-Moduls, allerdings ohne die ganzen Sonderfälle des Formats zu berücksichtigen.

Letztendlich ist das was Du da machst aber nur sehr schwer nachvollziehbar und wie gesagt, bestimmt nicht generisch genug für die erwartete Lösung. Das macht nicht den Eindruck als wärst Du da systematisch heran gegangen. Also das Problem in kleinere Teilprobleme zu zerlegen und die dann getrennt und gezielt zu lösen.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

@BlackJack, aber wenn ich nur "(self.titles, self.cols) == (other.titles, other.cols)" in der "__eq__" habe, dann muss ich doch zwangsläufig auch etwas haben wenn ich "my_table3_parsed == my_table3" vergleiche, da eines der Objekte ja nicht über "title" und "cols" definiert wird?
Ich hab es schon mit dem csv-Modul versucht, bekomme aber immer etwas wie "list object is not callable", deswegen war ich froh das es mit dem jetzigen Aufruf endlich geklappt hatte, außerhalb der Klasse.
BlackJack

@Ambriador: Beide Objekte werden über `titles` und `cols` bestimmt. Wenn sie das nicht sind, dann ist *das* der/ein Fehler. Der einzige Unterschied ist wie diese Attribute zustande kommen. Das ist zum Zeitpunkt des Vergleichs aber egal, denn dann haben sie ja die gleichen Werte.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

@BlackJack
wenn ich nur "(self.titles, self.cols) == (other.titles, other.cols)" stehen lasse bekomme ich den Error "'Table' object has no attribute 'titles'". Ich sehe auch gerade das er für die aus der Datei gelesenen Liste nur "([[], []],)" ausgibt, also funktioniert die "parse_csv" doch nicht richtig :| :?:
Antworten