Ist es besser, Attribute in einer Klasse vorab zu initialisieren oder sie unterwegs hinzuzufügen?


12

Es tut mir leid, wenn dies eine ABSOLUT sophomorische Frage ist, aber ich bin gespannt, welche Best Practices es gibt, und ich kann bei Google anscheinend keine gute Antwort finden.

In Python verwende ich normalerweise eine leere Klasse als Super-Catchall-Datenstrukturcontainer (ähnlich einer JSON-Datei) und füge unterwegs Attribute hinzu:

class DataObj:
    "Catch-all data object"
    def __init__(self):
        pass

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

Dies gibt mir eine enorme Flexibilität, da das Containerobjekt im Wesentlichen alles speichern kann. Wenn also neue Anforderungen auftauchen, füge ich sie einfach als weiteres Attribut zum DataObj-Objekt hinzu (das ich in meinem Code weitergebe).

Vor kurzem hat mich (von FP-Programmierern) beeindruckt, dass dies eine schreckliche Praxis ist, da es sehr schwierig ist, den Code zu lesen. Man muss den gesamten Code durchgehen, um herauszufinden, welche Attribute DataObj tatsächlich hat.

Frage : Wie kann ich dies für eine bessere Wartbarkeit umschreiben, ohne die Flexibilität zu beeinträchtigen?

Gibt es Ideen aus der funktionalen Programmierung, die ich übernehmen kann?

Ich suche da draußen nach Best Practices.

Hinweis : Eine Idee besteht darin, die Klasse mit allen Attributen zu initialisieren, auf die man voraussichtlich stoßen wird, z

class DataObj:
    "Catch-all data object"
    def __init__(self):
        data.a = 0
        data.b = ""
        data.c = []

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

Ist das eigentlich eine gute Idee? Was ist, wenn ich nicht weiß, was meine Attribute a priori sind?


Ihre Datenstrukturen sind so veränderlich, dass Sie anscheinend mit ihrer Wartbarkeit befasst sind. Lesen Sie in Ihrer ausgiebigen Freizeit ™ diesen Artikel über unveränderliche Datenmodelle . Dies könnte die Art und Weise, wie Sie über Daten argumentieren, vollständig ändern.
9000

@ 9000 Ein solcher Artikel überzeugt die bereits Überzeugten erneut. Für mich schien es eher ein How-to als ein Why zu sein (Die Liste der Gründe ist nicht wirklich überzeugend, es sei denn, Sie haben das Gefühl, diese spezifischen Bedürfnisse zu haben). Für mich ist es nicht überzeugend, dass jemand, der Rechnungen in VB aktualisiert, ständig neue Kopien seines Rechnungsobjekts erstellen muss (Zahlung hinzufügen, neues Rechnungsobjekt hinzufügen; Teil hinzufügen, neues Rechnungsobjekt).
Paul

Antworten:


10

Wie kann ich dies für eine bessere Wartbarkeit umschreiben, ohne die Flexibilität zu beeinträchtigen?

Das tust du nicht. Die Flexibilität ist genau das, was das Problem verursacht. Wenn ein Code irgendwo die Attribute eines Objekts ändern kann, ist die Wartbarkeit bereits in Teilen. Im Idealfall verfügt jede Klasse über eine Reihe von Attributen, die nachträglich in Stein gemeißelt sind __init__und für jede Instanz gleich sind. Nicht immer möglich oder sinnvoll, aber es sollte immer dann der Fall sein, wenn Sie keine wirklich guten Gründe haben, dies zu vermeiden.

Eine Idee besteht darin, die Klasse mit allen Attributen zu initialisieren, auf die man voraussichtlich stoßen wird

Das ist keine gute Idee. Sicher, dann ist das Attribut vorhanden, kann aber einen falschen Wert haben oder sogar einen gültigen, der den Code verdeckt, der den Wert nicht zuweist (oder einen falsch geschriebenen). AttributeErrorist beängstigend, aber falsche Ergebnisse zu erzielen ist schlimmer. Standardwerte sind im Allgemeinen in Ordnung, aber um einen sinnvollen Standard zu wählen (und zu entscheiden, was erforderlich ist), müssen Sie wissen, wofür das Objekt verwendet wird.

Was ist, wenn ich nicht weiß, was meine Attribute a priori sind?

Dann sind Sie auf jeden Fall fertig und sollten ein Diktat oder eine Liste anstelle von fest codierten Attributnamen verwenden. Aber ich nehme an, Sie meinten "... zu der Zeit, als ich die Containerklasse schreibe". Dann lautet die Antwort: "Sie können Dateien im Gleichschritt bearbeiten, duh." Benötigen Sie ein neues Attribut? Fügen Sie der Containerklasse ein verdammtes Attribut hinzu. Es gibt mehr Code, der diese Klasse verwendet und dieses Attribut nicht benötigt? Teilen Sie die Dinge in zwei separate Klassen auf (verwenden Sie Mixins, um trocken zu bleiben). Machen Sie es also optional, wenn es sinnvoll ist.

Wenn Sie Angst haben, sich wiederholende Containerklassen zu schreiben: Wenden Sie die Metaprogrammierung mit Bedacht an oder verwenden collections.namedtupleSie sie , wenn Sie die Mitglieder nach der Erstellung nicht mutieren müssen (Ihre FP-Freunde würden sich freuen).


7

Sie können immer Alex Martellis Bunch-Klasse verwenden . In deinem Fall:

class DataObj:
    "Catch-all data object"
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

def processData(inputs):
    data = DataObj(a=1, b="sym", c=[2,5,2,1])

Auf diese Weise ist dem Leser zumindest klar, dass Daten nur ein dummer Datenspeicher sind und man sofort sehen kann, welche Werte unter welchem ​​Namen gespeichert sind, da alles in einer Zeile geschieht.

Und ja, manchmal ist es sogar eine gute Idee, Dinge auf diese Weise zu tun.


1

Ich würde wahrscheinlich den zweiten Ansatz verwenden, möglicherweise Noneum ungültige Daten anzuzeigen. Es ist wahr, dass es schwierig ist zu lesen / zu pflegen, wenn Sie später Attribute hinzufügen. Weitere Informationen zum Zweck dieser Klasse / dieses Objekts geben jedoch Aufschluss darüber, warum die erste Idee ein schlechtes Design ist: Wo würden Sie jemals eine vollständig leere Klasse ohne Methoden oder Standarddaten haben? Warum wissen Sie nicht, welche Attribute die Klasse hat?

Es ist möglich, dass processDatadies als Methode besser ist ( process_dataum Python-Namenskonventionen zu befolgen), da sie auf die Klasse einwirkt. In Anbetracht des Beispiels sieht es so aus, als wäre es als Datenstruktur besser (wobei a dictausreichen könnte).

Anhand eines realen Beispiels können Sie die Frage an CodeReview weiterleiten , wo sie zur Umgestaltung des Codes beitragen können.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.