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?
Antworten:
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.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).
AttributeError
ist 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.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.namedtuple
Sie sie , wenn Sie die Mitglieder nach der Erstellung nicht mutieren müssen (Ihre FP-Freunde würden sich freuen).quelle
Sie können immer Alex Martellis Bunch-Klasse verwenden . In deinem Fall:
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.
quelle
Ich würde wahrscheinlich den zweiten Ansatz verwenden, möglicherweise
None
um 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
processData
dies als Methode besser ist (process_data
um 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 adict
ausreichen könnte).Anhand eines realen Beispiels können Sie die Frage an CodeReview weiterleiten , wo sie zur Umgestaltung des Codes beitragen können.
quelle