Liste in Methode erzeugen und zurück geben

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
tz_wuerzburg
User
Beiträge: 71
Registriert: Dienstag 7. März 2017, 17:51

Hallo zusammen,
ich benötige bitte Eure Hilfe. Wie ist es möglich, alle eingelesenen 'lines' mit dem return zurück zu geben.
Die 'workfile.csv' Datei sieht so aus, TAB getrennt:
Vorname Nachname
Max Mustermann
Max Mustermann

Das hätte ich gerne auch Zeile für Zeile durch die Methode 'read_tab_utf8_file()' zurück. Geht das, oder habe ich da einen Denkfehler?
Danke!

Code: Alles auswählen

import csv

class FileReader:
    def __init__(self, filename):
        self.filename = filename

    def read_tab_utf8_file(self):
        lines = []
        with open(self.filename, "r", encoding="utf-8") as input_file:
            input_file = csv.reader(input_file, delimiter='\t')
            for line in input_file:
                lines = line
            return lines

f = FileReader('workfile.csv')
print(f.read_tab_utf8_file())
Das Wunschergebnis (als Liste):
['Vorname', 'Nachname']
['Max', 'Mustermann']
['Max', 'Mustermann']
Benutzeravatar
sparrow
User
Beiträge: 4532
Registriert: Freitag 17. April 2009, 10:28

Die Klasse FileReader ist unnötig. Die Methode read_tab_utf8_file sollte eine eigenständige Funkton sein, die filename als Parameter bekommt.

Was macht dein Code denn anders als du erwartest?

lines ist eine Liste. Um etwas an eine Liste anzuhängen verwendet man append.
Benutzeravatar
__blackjack__
User
Beiträge: 14012
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Naja `lines` ist zwar eine Liste, aber nur mit dem letzten Datensatz. Es sollte auch eher `rows` und `row` heissen. Und beim öffnen fehlt das `newline`-Argument. Siehe Dokumentation vom `csv`-Modul.

Das die Datei `input_file` heisst ist okay, aber dann gleich danach das Reader-Objekt auch an diesen Namen zu binden ist verwirrend.

Das Einlesen kann man auch kompakter schreiben, denn statt mit einer leeren Liste zu starten und die Elemente vom Iterator mit einer ``for``-Schleife einfach nur in die Liste zu schaufeln, kann man einfach `list()` mit dem Iterator aufrufen.

Das ganze schrumpft auf drei Zeilen zusammen (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import csv


def read_file(filename):
    with open(filename, "r", encoding="utf-8", newline="") as file:
        return list(csv.reader(file, delimiter="\t"))


def main():
    print(read_file("workfile.csv"))


if __name__ == "__main__":
    main()
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
tz_wuerzburg
User
Beiträge: 71
Registriert: Dienstag 7. März 2017, 17:51

Vielen Dank für Eure Hinweise, das hat sehr geholfen!

Code: Alles auswählen

import csv

class EditFile:
    def __init__(self, filename):
        self.filename = filename

    def read_tab_utf8_file(self):
        with open(self.filename, "r", encoding="utf-8", newline="") as file:
            return list(csv.reader(file, delimiter="\t"))

    def set_id(self, b):
        zaehler = 0
        for row in b:
            zaehler = zaehler + 1
            table = row.append(zaehler)
            print(row)


f = EditFile('workfile.csv')
b = f.read_tab_utf8_file()
f.set_id(b)
Ist die Schreibweise die Richtige? Also eine Variable 'b' zu erzeugen, um das Objekt ' f.read_tab_utf8_file()' darin zu speichern, um den return Wert daraus weiter verarbeiten zu können?
Gibt es eine elegantere Lösung oder ist das der richtige Weg?
Benutzeravatar
__blackjack__
User
Beiträge: 14012
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@tz_wuerzburg: Warum ist denn jetzt schon wieder diese unsinnige Klasse da? `read_tab_utf8_file()` kann wie schon gesagt den Dateinamen als Argument bekommen und `set_id()` ist nicht mal eine Methode. Das ist einfach eine Funktion die nichts mit dieser Klasse zu tun hat. `EditFile` ist auch schon als Name ein Hinweis das da etwas komisch ist, denn das klingt nicht nach einem ”Ding” sondern nach einer Tätigkeit. Also das wofür man Funktionen (und Methoden) hat.

`f` und `b` sind schlechte Namen, weil die nichts darüber verraten was die Werte dahinter bedeuten.

`zaehler` manuell zu verwalten ist unschön, dafür gibt es die `enumerate()`-Funktion.

`table` wird nirgends verwendet, und ist auch ein komischer Name für `None`, denn `append()` hat keinen Rückgabewert.

Code: Alles auswählen

#!/usr/bin/env python3
import csv


def read_tab_utf8_file(filename):
    with open(filename, "r", encoding="utf-8", newline="") as file:
        return list(csv.reader(file, delimiter="\t"))


def append_id(rows):
    for zaehler, row in enumerate(rows, 1):
        row.append(zaehler)


def main():
    rows = read_tab_utf8_file("workfile.csv")
    append_id(rows)
    print(rows)


if __name__ == "__main__":
    main()
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
tz_wuerzburg
User
Beiträge: 71
Registriert: Dienstag 7. März 2017, 17:51

@__blackjack__: Ich versuche das mit den Klassen gerade zu lernen und habe mir ein Beispiel ausgesucht, welches für mich Sinn ergibt. Um zu zeigen wohin das führen soll, habe ich den Code erweitert. Es gibt jetzt auch eine Methode um einfach Semikolongetrennte Files zu lesen.
Habe ich das so falsch verstanden? Eine Klasse enthält eine Methode. Eine Methode ist der Bauplan um ein Objekt zu erstellen. Mein Objekt ist das File und das Programm hat nun zwei verschiedene Methoden um das einzulesen.

Code: Alles auswählen

import csv

class ReadFile:
    def __init__(self, filename):
        self.filename = filename

    def read_tab_utf8_file(self):
        with open(self.filename, 'r', encoding='utf-8', newline='') as file:
            return list(csv.reader(file, delimiter="\t"))

    def read_semicolon_utf8_file(self):
        with open(self.filename, 'r', encoding='utf-8', newline='') as file:
            return list(csv.reader(file, delimiter=';'))

    def append_row_id(self, rows):
        rows_with_id = []
        for zaehler, row in enumerate(rows, 1):
            row.append(zaehler)
            rows_with_id.append(row)
        return rows_with_id

# Einlesen einer Semikolon getrennten Datei. Erzeugen einer ID pro Zeile.
f = ReadFile('workfile_tab.txt')
imported_file = f.read_tab_utf8_file()
f.append_row_id(imported_file)

# Einlesen einer TAB getrennten Datei. Erzeugen einer ID pro Zeile.
f = ReadFile('workfile_semicolon.csv')
imported_file = f.read_semicolon_utf8_file()
f.append_row_id(imported_file)
Benutzeravatar
__blackjack__
User
Beiträge: 14012
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@tz_wuerzburg: Auch diese Klasse ergibt keinen Sinn. Der einzige Zustand ist der Dateiname und den kann man wie auch vorher schon, einfach als Argument an die nun zwei ”Methoden” übergeben und die dritte ”Methode” ist immer noch komplett falsch in dieser ”Klasse”.

Wenn eine Klasse *eine* Methode enthält (und die nicht sinnvoll andere geerbte Methoden ergänzt oder dort eine überschreibt), dann ist es schon mal fragwürdig ob das überhaupt eine Klasse ist.

Methoden sind kein Bauplan für Objekte, Klassen sind das.

Die neu hinzugekommene Methode ist auch unsinnig weil die ja genau das gleiche wie die andere Methode zum einlesen macht, nur mit einer Änderung die total simpel parametrisierbar ist. Das sind im Endeffekt statt einer unsinnigen Klasse immer noch nur zwei Funktionen.

`append_row_id()` hast Du jetzt verschlimmbessert. Die verändert die übergebene Datenstruktur und gibt eine neue Liste zurück. Sie sollte eines von beidem machen: Entweder nur die übergebene Datenstruktur ändern, oder aber eine neue zurückgeben aber nichts an der alten ändern. So wie es jetzt ist enthalten beide Datenstrukturen am Ende die gleichen Daten. Dafür braucht man keine neue Liste anlegen und es verwirrt auch den Benutzer.

Code: Alles auswählen

#!/usr/bin/env python3
import csv


def read_utf8_file(filename, delimiter):
    with open(filename, "r", encoding="utf-8", newline="") as file:
        return list(csv.reader(file, delimiter=delimiter))


def append_row_id(rows):
    for zaehler, row in enumerate(rows, 1):
        row.append(zaehler)


def main():
    rows = read_utf8_file("workfile_tab.txt", "\t")
    append_row_id(rows)

    rows = read_utf8_file("workfile_semicolon.csv", ";")
    append_row_id(rows)


if __name__ == "__main__":
    main()
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
tz_wuerzburg
User
Beiträge: 71
Registriert: Dienstag 7. März 2017, 17:51

@__blackjack__: Ok verstanden, ich brauche ein besseres Beispiel um mir die OOP näher zu bringen und werde es bestimmt auch finden.
Ich schreibe es in eine neue Liste weil das return sonst nur das letzte row ausgibt.

Code: Alles auswählen

    
def append_row_id(self, rows):
	rows_with_id = []
	for zaehler, row in enumerate(rows, 1):
		row.append(zaehler)
		rows_with_id.append(row)
	return rows_with_id
Das gibt mir die Werte wie gewünscht, aber ich kann mir nicht erklären warum.

Code: Alles auswählen

def append_row_id(self, rows):
	for zaehler, row in enumerate(rows, 1):
		row.append(zaehler)
	return rows
Benutzeravatar
__blackjack__
User
Beiträge: 14012
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@tz_wuerzburg: Da gehört überhaupt kein ``return`` rein. Die Funktion *verändert* die übergebenen `rows`.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Antworten