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.
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ambriador: jetzt fängst Du solangsam an, die richtigen Fragen stellen. Es ist ein Fehler, dass das Table-Objekt keine Attribute titles und cols hat. Du mußt dafür sorgen, dass diese Attribute in »__init__« angelegt werden. Eigentlich sollte man nicht nur durch Zufall merken, dass die Funktion nicht das macht, was man eigentlich denkt, was sie macht (obwohl ich das heute schon um 18:13Uhr und BlackJack um 19:18Uhr geschrieben haben). Sondern man schreibt Tests, die das auch überprüfen.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

@Sirius3 wie gesagt, einzeln hatte meine Funktion funktioniert, ich dachte ich muss sie dann einfach nur noch aufrufen und fertig, aus euren Kommentaren ist mir das erst nicht so schlüssig geworden. Dann versuche ich mal in der Nachtschicht was zu basteln.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Also wenn ich das richtig verstanden habe darf ich in der "__init__" nicht unterscheide mit einem "if/else" sondern muss ein objekt erstellen, aber wie lasse ich die "__init__" denn erkennen ob es sich um ein file oder attribute handelt?
BlackJack

@Ambriador: Doch, Du musst in der `__init__()` testen und entscheiden was von beiden der Benutzer angegeben hat und was daraufhin passieren muss. Aber egal was der Aufrufer übergibt, *nachdem* die `__init__()` ausgeführt wurde und zum Aufrufer zurück kehrt muss das Objekt in einem benutzbaren Zustand sein. Das heisst die Attribute `headers` und `cols` *dieses* Objekts müssen die entsprechenden Daten enthalten.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Oh je ok, und wofür brauche ich dann die parse_csv? Hört sich so an als ob der Code zum bereitstellen ja schon in die "__init__" muss?
EDIT:
Sorry ist schon spät. Ich denke mal das es so ungefähr aussehen soll?

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 = filename
            else:
                self.file1 = self.parse_csv(filename)
    
    def __str__(self):
            return (str(self.titles) +', '+ str(self.cols))

       
    def __eq__(self, other):
            return (self.titles, self.cols) == (other.titles, other.cols)

                   
    def parse_csv(self, filename):
        with open(filename) as f:
            reader = csv.reader(f)
            header = next(reader)
            cols = list(zip(*reader))
            return [header, cols]
Jetzt sagt er mir aber "global name "open" is not definded", obwohl das doch zur csv gehört?
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Endlich habe ich was halbwegs vernünftiges:

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 = filename
            else:
                self.file1 = self.parse_csv(filename)
    
    def __str__(self):
            return (str(self.titles) +', '+ str(self.cols))

       
    def __eq__(self, other):
            return (self.titles, self.cols) == (other.titles, other.cols)

                   
    def parse_csv(self, filename):
        with open(filename) as f:
            reader = csv.reader(f)
            self.titles = next(reader)
            self.cols = list(zip(*reader))
            return [self.titles, self.cols]
Jetzt gibt er mir nur noch die "self.cols" als string und nicht als float aus.
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ambriador: das sieht doch jetzt schon mal gut aus. Und es fehlen nur noch ein paar Kleinigkeiten. Achte auf die korrekte Einrückung: 4 Leerzeichen pro Ebene. Setze immer alle Attribute in »__init__«. Für was ist »file1« da? Warum werden »titles« und »cols« nicht immer gesetzt? Für eine klare Schnittstelle sollte eine Methode entweder den internen Zustand eines Objektes ändern, oder etwas zurückliefern, aber nicht beides.
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

Vielen Vielen Dank für eure Hilfe, ich hab es endlich hinbekommen :)

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 = filename
        else:
            self.titles = []
            self.cols = []
            self.filename = self.parse_csv(filename)
    
    def __str__(self):
        return (str(self.titles) +', '+ str(self.cols))

       
    def __eq__(self, other):
        return (self.titles, self.cols) == (other.titles, other.cols)

                   
    def parse_csv(self, filename):
        with open(filename) as f:
            reader = csv.reader(f)
            self.titles = next(reader)
            self.cols = [list(a) for a in zip(*reader)]
            self.cols = [[float(b) for b in c] for c in self.cols]
            return [self.titles, self.cols]
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ambriador: nein, hast Du nicht. Was Du an das Attribut »filename« bindest ist kein Dateiname.

Daneben liefert »parse_csv« etwas zurück und ändert Attribute. Das sollte nicht sein. Zeile 26 könnte man sich auch sparen. Außerdem solltest Du erklären können, was »zip(*reader)« genau macht, nicht dass Dein Lehrer auf die Idee kommt, Du könntest das irgendwo abgeschrieben haben.

Code: Alles auswählen

import csv
 
class Table:
    def __init__(self, titles=None, cols=None, filename=None):
        self.titles = titles
        self.cols = cols
        if filename is not None:
            self.parse_csv(filename)
   
    def __str__(self):
        return "{}, {}".format(self.titles, self.cols)
 
    def __eq__(self, other):
        return self.titles == other.titles and self.cols == other.cols
                   
    def parse_csv(self, filename):
        with open(filename) as f:
            reader = csv.reader(f)
            self.titles = next(reader)
            self.cols = [[float(b) for b in c] for c in zip(*reader)]
Ambriador
User
Beiträge: 35
Registriert: Freitag 25. November 2016, 01:05

AH ok ,war mir nicht klar das man das auch so machen kann.
Wieso ist es nicht so gut wenn parse_csv etwas zurück gibt?
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@Ambriador: eine Methode sollte genau eine Sache machen. Z.B. entweder etwas Setzen, oder einen Wert zurückliefern. Macht sie beides, verwirrt das nur, wie Dich z.B., da Du Dich ja gezwungen gefühlt hast, den Rückgabewert an irgendetwas (self.filename) zu binden, was so gar keinen Sinn gemacht hat.
BlackJack

Damit die API nicht so stehen bleibt, hier mal ein Beispiel wie so eine Klasse eher aussehen sollte:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import csv
import io
from itertools import izip

CSV_SOURCE = '''\
Zeit,Temperatur
0,15.2
3,16.1
6,14.8
9,20.2
'''

class Table(object):

    def __init__(self, headers, columns):
        self.headers = headers
        self.columns = columns

    def __repr__(self):
        return '{0.__class__.__name__}({0.headers!r}, {0.columns!r})'.format(
            self
        )

    def __eq__(self, other):
        try:
            return (
                (self.headers, self.columns) == (other.headers, other.columns)
            )
        except AttributeError:
            return NotImplemented

    def __ne__(self, other):
        return not self == other

    def __hash__(self):
        return TypeError('unhashable type: ' + self.__class__.__name__)

    @classmethod
    def read_csv(cls, csv_file):
        reader = csv.reader(csv_file)
        headers = next(reader)
        columns = [map(float, row) for row in izip(*reader)]
        return cls(headers, columns)

    @classmethod
    def load_csv(cls, filename):
        with open(filename) as csv_file:
            return cls.read_csv(csv_file)


def main():
    table_a = Table(
        ['Zeit', 'Temperatur'], [[0.0, 3.0, 6.0, 9.0], [15.2, 16.1, 14.8, 20.2]]
    )
    table_b = Table.read_csv(io.BytesIO(CSV_SOURCE))
    table_c = Table.load_csv('test.csv')
    print(table_a)
    print(table_b)
    print(table_c)
    print(table_a == table_b == table_c)


if __name__ == '__main__':
    main()
Antworten