Vererbung - Eigenschaften der __init__ Methode

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
DrRocket
User
Beiträge: 30
Registriert: Freitag 11. Mai 2018, 15:11

Hallo zusammen,

ich habe eine Frage zur Funktionsweise der Vererbung. Insbesondere geht es um die in der Elternklasse deklarierten Eigenschaften und Methoden, die ich am nachfolgenden Beispiel gerne verstehen würde:

Code: Alles auswählen

class FileReader():

    def __init__(self, path):
        self.path = path
        self.__file_as_list = []
        self.line = None

    def lines(self):
        self.__file_as_list.clear()
        with open(self.path, "r") as file:
            for i in file:
                self.line = i.strip()
                self.__file_as_list.append(self.line)

        return self.__file_as_list

class CsvReader(FileReader):

    def __init__(self, path):
        self.path = path
        self.__file_as_list = []
        self.line = None
        # super().__init__(path)

    def lines(self):
        self.__file_as_list.clear()
        with open(self.path, "r") as file:
            for i in file:
                self.line = i.strip().split(",")
                self.__file_as_list.append(self.line)
        return self.__file_as_list
Mein Verständnis nach einem Tutorial war, dass ich in der Kindklasse (im Beispiel CsvReader) nicht erneut die __init__ Methode definieren muss, da diese identisch ist zu der Elternklasse (FileReader). Nun funktioniert die Kindklasse nicht wie erwartet. Ohne die __init__ Methode dort explizit zu definieren, kommt immer die Fehlermeldung: "AttributeError: 'CsvReader' object has no attribute '_CsvReader__file_as_list'". Auch wenn ich __init__in der Kindklasse definiere und mit super() versuche die Eigenschaften der Elternklasse zu übernehmen, bekomme ich die gleiche Fehlermeldung. Nur wenn ich den Inhalt komplett erneut definiere, läuft die Kindklasse.

Kann mir hier jemand auf die Sprünge helfen? Warum muss ich die __init__ Methode erneut in der Kindklasse erstellen. Sollte die Vererbungnicht gerade diese Redundanten Codestücke vermeiden?
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Tut sie auch. Nur hast du dich gleichzeitig entschieden, einem ungeeigneten Muster zu folgen, welches man of sieht bei Leuten, die aus anderen Sprachen kommend OO in Python anwenden: dein Problem ist der doppelte unterstrich. Dadurch wird der Name deines Attributes __file_as_list implizit mit dem umgebenden Klassennamen als Präfix versehen. Und erreicht damit genau, was du nicht willst: Unsichtbarkeit in der abgeleiteten Klasse.

Benenn die Eigenschaft um, mit nur EINEM unterstrich, und du sparst dir den extra Initialisator.
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DrRocket: Mit den *zwei* führenden Unterstrichen sagst Du explizit das dieses Attribut nur zu *genau der Klasse* gehört und nicht in Unterklassen benutzt werden kann/soll. Das braucht man nur äusserst selten, also mach da einfach *einen* führenden Unterstrich draus.

Das `line`-Attribut erscheint mir nicht sinnvoll und statt die Liste zu leeren würde ich da einfach eine neue erstellen. Und das mit einer „list comprehension“ und nicht in einer Schleife. Dann wird das alles viel kompakter.

`i` für etwas anderes als ganze Zahlen zu verwenden, insbesondere in einer Schleife, ist sehr verwirrend.

Das `lines()` eine Datei einliest, und das bei jedem Aufruf, *und* dann auch noch an den Aufrufer zurück gibt, ist unerwartet. Üblich ist das eine Methode den Zustand eines Objekts verändert *oder* etwas an den Aufrufer zurück gibt.

Die Liste die Du als Implementierungsdetail definierst an den Aufrufer raus zu geben ist auch nicht so sinnig. Dann kann man sich auch den einen führenden Unterstrich sparen.

Edit:

Code: Alles auswählen

import csv


class FileReader(object):

    def __init__(self, path):
        self.path = path
        self.lines = list()

    def read(self):
        with open(self.path, 'r') as file:
            self.lines = [line.strip() for line in file]


class CsvReader(FileReader):

    def read(self):
        with open(self.path, 'r') as file:
            self.lines = list(csv.reader(file))
Ich bin da aber nicht so überzeugt, weil die Dinger kaum etwas machen, CSV eigenllich keine „lines“ sondern „rows“ oder „records“ enthalten, und damit die ein „`CsvReader` ist ein `FileReader`“-Beziehung nicht mehr wirklich hin haut, und das die Vererbung stark in Frage stellt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Und da gibt's dann noch den „code smell“ bei Klassen die neben der `__init__()` nur noch eine weitere Methode haben: Man sollte sicher sein, dass das nicht einfach umständlich geschriebene Funktionen sind:

Code: Alles auswählen

import csv

def read_lines(path):
    with open(path, 'r') as file:
        return [line.strip() for line in file]


def read_csv(path):
    with open(path, 'r') as file:
        return list(csv.reader(file))
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
DrRocket
User
Beiträge: 30
Registriert: Freitag 11. Mai 2018, 15:11

Ok, danke für die guten Tipps. Ich werde mich da nachher noch ran setzen und die beiden Klassen entsprechend optimieren.

@__blackjack__: Das Ganze dient lediglich der Übung und dem Verständnis von OOP in Python. Einen anderen Sinn haben die beiden Beispiele hier nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13069
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DrRocket: Hm, aber zum Verständnis von OOP gehört doch auch wann und wie OOP sinnvoll ist. Spätestens wenn Du die Klassen „optimierst“, sind sie weg, weil dann nur noch die beiden Funktionen übrig bleiben. Und ich sehe keine Vererbungsbeziehung zwischen den beiden Klassen. Eine CSV-Datei ist keine Datei mit Zeilen, sondern eine mit Datensätzen aus Spaltenwerten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten