Liste als Rückgabewert eine Klassenmethode

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
Hartmannsgruber
User
Beiträge: 89
Registriert: Mittwoch 15. Januar 2014, 22:30
Wohnort: Bad Kötzting
Kontaktdaten:

Servus liebes Forum,

ich bitte um Entschuldigung für meine wohl doch sehr "einfache" und wohl auch dämliche Frage...
Leider ist noch kein Meister vom Himmel gefallen und aller Anfang ist bekanntlich schwer :-(

Code: Alles auswählen

#! usr/bin/python3

import csv

from prettytable import PrettyTable

class Arbeitsplan():

    def __init__(self):
        self.arbeiter = []
        self.tabelle = PrettyTable()
        
    def arbeitnehmer_erfassen(self, arbeitnehmer_csv_datei):
        with open(arbeitnehmer_csv_datei, encoding="utf-8") as csv_datei:
            reader = csv.reader(csv_datei, delimiter=";")

            self.tabelle.field_names = next(reader)

            for zeile in reader:
                self.arbeiter.append(Arbeitnehmer(
                    zeile[0], #Vorname
                    zeile[1], #Nachname
                    zeile[2], #Beruf
                    int(zeile[3]), #Arbeitsstunden pro Tag
                    int(zeile[4]), #Arebitstage pro Woche
                    int(zeile[5])  #Urlaubstage pro Jahr
                    ))

    def arbeitnehmer_anzeigen(self):
        print(self.tabelle)
        
        for arbeiter in self.arbeiter:
            self.tabelle.add_row(arbeiter)
    

class Arbeitnehmer():
    def __init__(self, vorname, nachname, beruf, arbeitsstunden, arbeitstage, urlaub):
       self.eckdaten = [
            ("Vorname", vorname),
            ("Nachname", nachname),
            ("Beruf", beruf),
            ("Arbeitsstunden", arbeitsstunden),
            ("Arbeitstage", arbeitstage),
            ("Urlaubstage", urlaub)
            ]


def main():
        Hotel = Arbeitsplan()
        Hotel.arbeitnehmer_erfassen("Arbeitnehmer.csv")

        Hotel.arbeitnehmer_anzeigen()

if __name__ == "__main__":
    main()
bei Ausführung des Codes erhalte ich die Fehlermeldung:

Code: Alles auswählen

TypeError: object of type 'Arbeitnehmer' has no len()
Der Fehler mir an sich ist klar, PrettyTable braucht eine Liste um diese zu verabeiten.
Wie bekomme ich aber jetzt die Instanz der Klasse Arbeitnehmer als Liste zurückgegeben?
Habe schon ein paar Möglicheiten ausprobiert, erhalte aber immer eine Fehlermeldung :-(

Ich bitte um Hilfe, da ich selbst nicht hinter die Lösung komme.
In der Internetsuche habe ich leider nichts passendes zu meinen Problem gefunden...

LG
Sirius3
User
Beiträge: 18272
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Lösung ist einfach: Arbeitnehmer muß ein Objekt sein, das sich wie eine Liste verhält, beziehungsweise hier eher ein Tuple.
Und Python hat schon etwas vorgefertigtes für genau solche Fälle: `namedtuple`.

Code: Alles auswählen

import csv
from collections import namedtuple
from prettytable import PrettyTable

Arbeitnehmer = namedtuple("Arbeitnehmer", "vorname, nachname, beruf, arbeitsstunden, arbeitstage, urlaub")

class Arbeitsplan():
    def __init__(self):
        self.arbeiter = []
        self.field_names = None
        
    def arbeitnehmer_erfassen(self, arbeitnehmer_csv_datei):
        with open(arbeitnehmer_csv_datei, encoding="utf-8") as csv_datei:
            reader = csv.reader(csv_datei, delimiter=";")
            self.field_names = next(reader)
            for zeile in reader:
                self.arbeiter.append(Arbeitnehmer(
                    zeile[0], #Vorname
                    zeile[1], #Nachname
                    zeile[2], #Beruf
                    int(zeile[3]), #Arbeitsstunden pro Tag
                    int(zeile[4]), #Arebitstage pro Woche
                    int(zeile[5])  #Urlaubstage pro Jahr
                    ))

    def generiere_arbeitnehmer_tabelle(self):
        tabelle = PrettyTable(self.field_names)
        for arbeiter in self.arbeiter:
            self.tabelle.add_row(arbeiter)

def main():
    hotel = Arbeitsplan()
    hotel.arbeitnehmer_erfassen("Arbeitnehmer.csv")
    print(hotel.generiere_arbeitnehmer_tabelle())

if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 14050
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hartmannsgruber: Bei der She-Bang-Zeile fehlt nach "!" und vor "user" ein "/", sonst funktioniert die nur wenn das Wurzelverzeichnis das aktuelle Arbeitsverzeichnis ist. Oder man im Arbeitsverzeichnis tatsächlich ein `usr/`-Unterverzeichnis hat das über `bin/` zu einem passenden `python3` führt.

Du musst halt entweder `Arbeitnehmer` eine passende API verpassen — also eine abfragbare Länge die immer 6 ist und mindestens Iterierbarkeit oder Indexzugriff, je nach dem was `PrettyTable` da erwartet. Oder Du musst Code schreiben der einen Arbeitnehmer in eine Sequenz umnwandeln kann und nicht den Arbeitnehmer direkt zum `PrettyTable`-Objekt hinzufügen sondern den in eine Liste umwandeln.

Am einfachsten wäre es ein `collections.namedtuple`-Typ für `Arbeitnehmer` zu erstellen.

Das das `PrettyTable`-Objekt beim Anzeigen mit Namen gefüllt wird, sorgt dafür das man den `Arbeitsplan` nur einmal anzeigen kann ohne dass die Namen dort mehrfach angehängt werden. Man würde das also eher dann erzeugen wenn man die Arbeitnehmer tatsächlich anzeigen möchte.

`Hotel` sollte klein geschrieben werden. Und vielleicht nicht `hotel` heissen, sondern `arbeitsplan`‽

`arbeitnehmer_erfassen()` klingt für mich eher nach Benutzerinteraktion. Ich würde das `arbeitnehmer_aus_csv_laden()` nennen. Und `arbeitnehmer_csv_datei` ist keine Datei sondern ein Datei*name*. Das ist ein Unterschied. Von etwas das `*_datei` heisst, erwartet man Methoden wie `read()` oder `write()` und `close()`. Das `arbeitnehmer_` braucht's auch nicht unbedingt, denn diese Information steckt ja bereits im Methodennamen.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
from collections import namedtuple
import csv

from prettytable import PrettyTable


Arbeitnehmer = namedtuple(
    "Arbeitnehmer",
    "vorname, nachname, beruf, arbeitsstunden, arbeitstage, urlaub",
)


class Arbeitsplan:
    def __init__(self):
        self.arbeiter = []

    def arbeitnehmer_aus_csv_laden(self, dateiname):
        with open(dateiname, encoding="utf-8") as csv_datei:
            reader = csv.reader(csv_datei, delimiter=";")
            next(reader)  # Kopfzeile überlesen.
            for zeile in reader:
                self.arbeiter.append(
                    Arbeitnehmer(
                        zeile[0],  # Vorname
                        zeile[1],  # Nachname
                        zeile[2],  # Beruf
                        int(zeile[3]),  # Arbeitsstunden pro Tag
                        int(zeile[4]),  # Arebitstage pro Woche
                        int(zeile[5]),  # Urlaubstage pro Jahr
                    )
                )

    def arbeitnehmer_anzeigen(self):
        table = PrettyTable(
            ["Vorname", "Name", "Beruf", "h/Tag", "Tage/Woche", "Urlaubstage"]
        )
        for arbeiter in self.arbeiter:
            table.add_row(arbeiter)
        print(table)


def main():
    arbeitsplan = Arbeitsplan()
    arbeitsplan.arbeitnehmer_aus_csv_laden("Arbeitnehmer.csv")

    arbeitsplan.arbeitnehmer_anzeigen()


if __name__ == "__main__":
    main()
Wobei ich so eine API zum *dazuladen* nicht so gut finde. Ich würde da mindestens eine Klassenmethode anbieten die Anlegen und reinladen der Daten als einen Schritt anbietet. Und vielleicht auch `arbeitnehmer` als Argument von `__init__()` übergeben, damit man leichter Tests schreiben kann.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten