Betrachten Sie die folgende Klasse:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
Meine Kollegen neigen dazu, es so zu definieren:
class Person:
name = None
age = None
def __init__(self, name, age):
self.name = name
self.age = age
Der Hauptgrund dafür ist, dass der Editor seiner Wahl die Eigenschaften für die automatische Vervollständigung anzeigt.
Persönlich mag ich letzteres nicht, weil es keinen Sinn macht, dass eine Klasse diese Eigenschaften hat None
.
Welches wäre eine bessere Übung und aus welchen Gründen?
__init__
bereits vorhandenenNone
IDE automatisch vervollständigt. Außerdem verhindert die Verwendung, dass die IDE einen besseren Typ für das Attribut herleitet möglich).typing
Modul, mit dem Sie Hinweise auf die IDE und Linter geben können, wenn Sie so etwasself
. Selbst wenn sie in der Instanz nicht angezeigt werdenself.name
oderself.age
nicht zugewiesen wurden , werden sie nur in der Klasse angezeigt .__init__
self
Person
Antworten:
Ich nenne die letztere schlechte Praxis unter der Regel "das tut nicht, was Sie denken, dass es tut".
Die Position Ihres Mitarbeiters kann wie folgt umgeschrieben werden: "Ich werde eine Reihe von klassenstatischen quasi-globalen Variablen erstellen, auf die nie zugegriffen wird, die jedoch Platz in den Namespace-Tabellen der verschiedenen Klassen belegen (
__dict__
), damit meine IDE funktioniert etwas."quelle
-OO
, für die wenigen, die es brauchen.1. Machen Sie Ihren Code leicht verständlich
Code wird viel häufiger gelesen als geschrieben. Erleichtern Sie die Arbeit Ihres Code-Betreuers (möglicherweise auch Sie selbst im nächsten Jahr).
Ich kenne keine strengen Regeln, aber ich bevorzuge es , wenn ein zukünftiger Instanzstatus eindeutig als eindeutig deklariert wird. Absturz mit einem
AttributeError
ist schon schlimm genug. Es ist schlimmer, den Lebenszyklus eines Instanzattributs nicht klar zu erkennen. Die Menge an mentaler Gymnastik, die erforderlich ist, um mögliche Anrufsequenzen wiederherzustellen, die zur Zuweisung des Attributs führen, kann leicht nicht mehr trivial werden und zu Fehlern führen.Daher definiere ich normalerweise nicht nur alles im Konstruktor, sondern bemühe mich auch, die Anzahl der veränderlichen Attribute auf ein Minimum zu beschränken.
2. Mischen Sie keine Mitglieder auf Klassen- und Instanzebene
Alles, was Sie direkt in der
class
Deklaration definieren, gehört zur Klasse und wird von allen Instanzen der Klasse gemeinsam genutzt. Wenn Sie beispielsweise eine Funktion innerhalb einer Klasse definieren, wird diese zu einer Methode, die für alle Instanzen gleich ist. Gleiches gilt für Datenmitglieder. Dies ist völlig anders als die Instanzattribute, in denen Sie normalerweise definieren__init__
.Datenelemente auf Klassenebene sind am nützlichsten als Konstanten:
quelle
self
und müssen nichtself
weitergegeben werden, während Methoden auf Klassenebene ungebunden und aus heutiger Sicht reine Funktionen sinddef
und eine Instanz als erstes Argument akzeptieren. Das sind also verschiedene Dinge.AttributeError
Signal ist ein gutes als ein Fehler. Andernfalls würden Sie None verschlucken und bedeutungslose Ergebnisse erhalten. Besonders wichtig in dem vorliegenden Fall, in dem die Attribute in definiert sind__init__
, sodass ein fehlendes Attribut (das jedoch auf Klassenebene vorhanden ist) nur durch fehlerhafte Vererbung verursacht werden kann.None
und dieser Wert zum Zeitpunkt der Instanzkonstruktion keinen Sinn ergibt , haben Sie ein Problem in Ihrer Architektur und müssen den Lebenszyklus des Attributwerts oder seines Anfangswerts überdenken. Beachten Sie, dass Sie durch frühzeitiges Definieren Ihrer Attribute ein solches Problem erkennen können, noch bevor Sie den Rest der Klasse geschrieben haben, geschweige denn den Code ausgeführt haben.Persönlich definiere ich die Mitglieder in der __ init __ () -Methode. Ich habe nie darüber nachgedacht, sie im Klassenteil zu definieren. Aber was ich immer mache: Ich initialisiere alle Member in der __ init__ -Methode, auch die, die in der __ init__ -Methode nicht benötigt werden.
Beispiel:
Ich halte es für wichtig, alle Mitglieder an einem Ort zu definieren. Dadurch wird der Code besser lesbar. Es ist nicht so wichtig, ob es innerhalb von __ init __ () oder außerhalb von __ ist. Für ein Team ist es jedoch wichtig, sich zu mehr oder weniger demselben Codierungsstil zu verpflichten.
Oh, und Sie werden vielleicht bemerken, dass ich Mitgliedervariablen jemals das Präfix "_" hinzufüge.
quelle
_
: Um anzuzeigen, dass es privat ist! (Warum verwechseln so viele Leute in diesem Thread Python mit anderen Sprachen?)Dies ist eine schlechte Praxis. Sie brauchen diese Werte nicht, sie verstopfen den Code und können Fehler verursachen.
Erwägen:
quelle
Ich gehe dann mit "ein bisschen wie Docstrings" und erkläre dies für harmlos, solange es immer so
None
ist oder ein enger Bereich von anderen Werten, die alle unveränderlich sind.Es riecht nach Atavismus und exzessiver Bindung an statisch typisierte Sprachen. Und es ist kein guter Code. Aber es hat einen kleinen Zweck, der in der Dokumentation verbleibt.
Es dokumentiert die erwarteten Namen. Wenn ich also Code mit jemandem kombiniere und einer von uns 'Benutzername' und der andere 'Benutzername' hat, gibt es einen Hinweis auf den Menschen, dass wir getrennte Wege gegangen sind und nicht dieselben Variablen verwenden.
Durch Erzwingen der vollständigen Initialisierung als Richtlinie wird das Gleiche auf pythonischere Weise erreicht. Wenn sich jedoch tatsächlicher Code in der Richtlinie befindet
__init__
, können die verwendeten Variablen auf diese Weise besser dokumentiert werden .Offensichtlich ist das BIG-Problem hier, dass es die Leute dazu verleitet, mit anderen Werten als zu initialisieren
None
, was schlecht sein kann:Hinterlässt einen globalen Trace und erstellt keine Instanz für x.
Aber das ist in Python als Ganzes eher eine Eigenart als in dieser Praxis, und wir sollten bereits paranoid sein.
quelle