4 Stimmen

Python-Klassen-Syntax - ist das eine gute Idee?

Ich bin versucht, meine Python-Klassen so zu definieren:

class MyClass(object):
    """my docstring"""

    msg = None
    a_variable = None
    some_dict = {}

    def __init__(self, msg):
        self.msg = msg

Ist die Deklaration der Objektvariablen (msg, a_variable usw.) am Anfang, wie bei Java, gut oder schlecht oder gleichgültig? Ich weiß, es ist unnötig, aber immer noch verlockend zu tun.

8voto

Yacoby Punkte 52756

Die Definition von Variablen in der Klassendefinition macht die Variable für jede Instanz der Klasse zugänglich. In Java ausgedrückt heißt das ein bisschen wie wodurch die Variable statisch wird. Es gibt jedoch wesentliche Unterschiede, wie unten gezeigt wird.

class MyClass(object):
    msg = "ABC"

print MyClass.msg     #prints ABC
a = MyClass()
print a.msg           #prints ABC
a.msg = "abc"
print a.msg           #prints abc
print MyClass.msg     #prints ABC
print a.__class__.msg #prints ABC

Wie aus dem obigen Code ersichtlich, ist es nicht ganz dasselbe, da die Variable zwar über self.msg Wenn ihr ein Wert zugewiesen wird, wird dieser nicht der im Klassenbereich definierten Variablen zugewiesen.

Einer der Nachteile der Methode, die Sie verwenden, ist, dass sie zu Fehlern führen kann, da sie der Klasse versteckte Zustände hinzufügt. Angenommen, jemand vergisst self.msg = "ABC" aus dem Konstruktor (oder, realistischer, der Code wurde überarbeitet und nur eine der Definitionen wurde geändert)

a = MyClass()
print a.msg   #prints ABC

#somewhere else in the program
MyClass.msg = "XYZ"

#now the same bit of code leads to a different result, despite the expectation that it
#leads to the same result.
a = MyClass()
print a.msg   #prints XYZ

Es ist weitaus besser, die Definition von msg auf Klassenebene zu vermeiden, um die Probleme zu umgehen:

class MyClass(object):
    pass

print MyClass.msg #AttributeError: type object 'MyClass' has no attribute 'msg'

6voto

AndiDog Punkte 65445

Durch die Deklaration von Variablen direkt innerhalb der Klassendefinition werden sie zu Klassenvariablen und nicht zu Instanzvariablen. Klassenvariablen ähneln in gewisser Weise den statischen Variablen in Java und sollten verwendet werden wie MyClass.a_variable . Sie können aber auch wie folgt verwendet werden self.a_variable was ein Problem darstellt, da naive Programmierer sie als Instanzvariablen behandeln können. Ihre Variable "some_dict" würde zum Beispiel von jeder Instanz von MyClass Wenn Sie also einen Schlüssel "k" hinzufügen, wird dieser für jede Instanz sichtbar.

Wenn Sie immer daran denken, Klassenvariablen neu zuzuweisen, gibt es fast keinen Unterschied zu Instanzvariablen. Nur die anfängliche Definition in MyClass werden bleiben. Aber das ist keine gute Praxis, da Sie in Schwierigkeiten geraten könnten, wenn Sie diese Variablen nicht neu zuweisen!

Schreiben Sie die Klasse besser so:

class MyClass(object):
    """
    Some class
    """

    def __init__(self, msg):
        self.__msg = msg
        self.__a_variable = None
        self.__some_dict = {}

Die Verwendung von zwei Unterstrichen für "private" Variablen ( pseudo-privat! ) ist optional. Wenn die Variablen öffentlich sein sollen, behalten Sie einfach ihre Namen ohne das __ Vorwahl.

4voto

unutbu Punkte 769083

Vorsichtig. Die beiden msg-Attribute sind eigentlich in zwei verschiedenen Wörterbüchern gespeichert. Das eine überschattet das andere, aber das geklammerte msg Attribut noch immer Platz in einem Wörterbuch einnimmt. Es bleibt also ungenutzt und nimmt dennoch etwas Speicherplatz in Anspruch.

class MyClass(object):    
    msg = 'FeeFiFoFum'   
    def __init__(self, msg):
        self.msg = msg

m=MyClass('Hi Lucy')

Beachten Sie, dass wir 'Hi Lucy' als Wert.

print(m.__dict__)
# {'msg': 'Hi Lucy'}

Beachten Sie, dass das Diktat von MyClass (Zugriff über m.__class__ ) hat noch FeeFiFoFum .

print(m.__class__.__dict__)
# {'__dict__': <attribute '__dict__' of 'MyClass' objects>, '__module__': '__main__', '__init__': <function __init__ at 0xb76ea1ec>, 'msg': 'FeeFiFoFum', 'some_dict': {}, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': 'my docstring', 'a_variable': None}

Eine andere (vielleicht einfachere) Art, dies zu sehen:

print(m.msg)
# Hi Lucy
print(MyClass.msg)
# FeeFiFoFum

1voto

rob Punkte 35132

Wenn Sie eine Klasse deklarieren, analysiert Python ihren Code und stellt alles in den Namensraum der Klasse; dann wird die Klasse als eine Art Vorlage für alle von ihr abgeleiteten Objekte verwendet - aber jedes Objekt hat seine eigene Kopie der Referenz.
Beachten Sie, dass Sie immer einen Verweis haben; wenn Sie also das referenzierte Objekt ändern können, wird die Änderung an allen Stellen, an denen es verwendet wird, berücksichtigt. Der Steckplatz für die Mitgliedsdaten ist jedoch für jede Instanz eindeutig, und daher wird die Zuweisung an ein neues Objekt nicht an einem anderen Ort, an dem es verwendet wird, reflektiert.

Anmerkung: Michael Foord hat eine sehr schöner Blogeintrag darüber, wie die Instanziierung von Klassen funktioniert; wenn Sie an diesem Thema interessiert sind, empfehle ich Ihnen diese kurze Lektüre.

Für alle praktischen Zwecke gibt es jedoch zwei wesentliche Unterschiede zwischen Ihren beiden Ansätzen:

  1. Der Name ist bereits auf Klassenebene verfügbar, und Sie können ihn verwenden, ohne ein neues Objekt zu instanziieren; dies mag für die Deklaration von Konstanten in Namespaces praktisch sein, aber in vielen Fällen kann der Modulname bereits ein guter Name sein.
  2. Der Name wird auf Klassenebene hinzugefügt - das bedeutet, dass Sie ihn während der Unit-Tests möglicherweise nicht einfach spiegeln können, und dass Sie, wenn Sie eine teure Operation haben, diese genau im Moment des Imports erhalten.

Normalerweise betrachte ich bei der Überprüfung von Code die auf Klassenebene deklarierten Mitglieder mit einem gewissen Misstrauen; es gibt viele gute Anwendungsfälle für sie, aber es ist auch sehr wahrscheinlich, dass sie aus einer Art Gewohnheit aufgrund früherer Erfahrungen mit anderen Programmiersprachen dort sind.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X