798 Stimmen

Verwendung von @property gegenüber Getter und Setter

Welche Vorteile bietet die @property Notation gegenüber der klassischen Getter+Setter-Schreibweise? In welchen spezifischen Fällen/Situationen sollte ein Programmierer die eine Notation der anderen vorziehen?

Mit Eigenschaften:

class MyClass(object):
    @property
    def my_attr(self):
        return self._my_attr

    @my_attr.setter
    def my_attr(self, value):
        self._my_attr = value

Ohne Eigenschaften:

class MyClass(object):
    def get_my_attr(self):
        return self._my_attr

    def set_my_attr(self, value):
        self._my_attr = value

27voto

NeilenMarais Punkte 2621

Ich denke, beides hat seine Berechtigung. Ein Problem bei der Verwendung von @property ist, dass es schwierig ist, das Verhalten von Gettern oder Settern in Unterklassen mithilfe von Standardklassenmechanismen zu erweitern. Das Problem ist, dass die eigentlichen Getter/Setter-Funktionen in der Eigenschaft versteckt sind.

Sie können die Funktionen z.B. mit

class C(object):
    _p = 1
    @property
    def p(self):
        return self._p
    @p.setter
    def p(self, val):
        self._p = val

können Sie auf die Getter- und Setter-Funktionen als C.p.fget y C.p.fset , aber man kann nicht einfach die normalen Möglichkeiten der Methodenvererbung (z.B. super) nutzen, um sie zu erweitern. Nachdem Sie sich etwas mit den Feinheiten von super beschäftigt haben, können Sie kann in der Tat super auf diese Weise verwenden:

# Using super():
class D(C):
    # Cannot use super(D,D) here to define the property
    # since D is not yet defined in this scope.
    @property
    def p(self):
        return super(D,D).p.fget(self)

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for D'
        super(D,D).p.fset(self, val)

# Using a direct reference to C
class E(C):
    p = C.p

    @p.setter
    def p(self, val):
        print 'Implement extra functionality here for E'
        C.p.fset(self, val)

Die Verwendung von super() ist jedoch recht umständlich, da die Eigenschaft neu definiert werden muss und man den etwas kontra-intuitiven super(cls,cls)-Mechanismus verwenden muss, um eine ungebundene Kopie von p zu erhalten.

26voto

Hobblin Punkte 865

Die Verwendung von Eigenschaften ist für mich intuitiver und passt besser in den meisten Code.

Vergleich von

o.x = 5
ox = o.x

vs.

o.setX(5)
ox = o.getX()

ist für mich ganz klar, was leichter zu lesen ist. Auch Eigenschaften ermöglichen private Variablen viel einfacher.

13voto

fulmicoton Punkte 14574

Ich habe das Gefühl, dass es bei Eigenschaften darum geht, den Overhead des Schreibens von Gettern und Settern nur dann zu nutzen, wenn man sie tatsächlich braucht.

Die Java-Programmierkultur rät dringend dazu, niemals Zugriff auf Eigenschaften zu gewähren und stattdessen über Getter und Setter zu gehen, und zwar nur solche, die tatsächlich benötigt werden. Es ist ein bisschen umständlich, immer diese offensichtlichen Codestücke zu schreiben, und es fällt auf, dass sie in 70 % der Fälle nie durch eine nicht-triviale Logik ersetzt werden.

In Python, Menschen tatsächlich Pflege für diese Art von Overhead, so dass Sie die folgende Praxis zu umarmen:

  • Verwenden Sie zunächst keine Getter und Setter, wenn sie nicht benötigt werden
  • 使用方法 @property um sie zu implementieren, ohne die Syntax des restlichen Codes zu ändern.

12voto

Ich würde es vorziehen, in den meisten Fällen weder das eine noch das andere zu verwenden. Das Problem mit Eigenschaften ist, dass sie die Klasse weniger transparent machen. Dies ist vor allem dann ein Problem, wenn Sie eine Ausnahme von einem Setter auslösen würden. Zum Beispiel, wenn Sie eine Eigenschaft Account.email haben:

class Account(object):
    @property
    def email(self):
        return self._email

    @email.setter
    def email(self, value):
        if '@' not in value:
            raise ValueError('Invalid email address.')
        self._email = value

dann erwartet der Benutzer der Klasse nicht, dass die Zuweisung eines Wertes zu der Eigenschaft eine Ausnahme verursachen könnte:

a = Account()
a.email = 'badaddress'
--> ValueError: Invalid email address.

Infolgedessen kann die Ausnahme unbehandelt bleiben und sich entweder zu weit oben in der Aufrufkette ausbreiten, um ordnungsgemäß behandelt zu werden, oder zu einem sehr wenig hilfreichen Traceback führen, der dem Programmbenutzer präsentiert wird (was in der Welt von Python und Java leider allzu häufig vorkommt).

Ich würde auch die Verwendung von Gettern und Settern vermeiden:

  • weil es sehr zeitaufwändig ist, sie für alle Eigenschaften im Voraus festzulegen,
  • macht den Code unnötig länger, was das Verständnis und die Pflege des Codes erschwert,
  • wenn man sie nur bei Bedarf für Eigenschaften definieren würde, würde sich die Schnittstelle der Klasse ändern, was allen Benutzern der Klasse schaden würde

Anstelle von Eigenschaften und Gettern/Settern ziehe ich es vor, die komplexe Logik an gut definierten Stellen, z. B. in einer Validierungsmethode, auszuführen:

class Account(object):
    ...
    def validate(self):
        if '@' not in self.email:
            raise ValueError('Invalid email address.')

oder eine ähnliche Account.save-Methode.

Ich will damit nicht sagen, dass es keine Fälle gibt, in denen Eigenschaften nützlich sind, sondern nur, dass Sie besser dran sind, wenn Sie Ihre Klassen so einfach und transparent gestalten können, dass Sie sie nicht brauchen.

11voto

fiacre Punkte 1090

Ich bin überrascht, dass niemand erwähnt hat, dass Eigenschaften gebundene Methoden einer Deskriptorklasse sind, Adam Donohue y NeilenMarais kommen in ihren Beiträgen genau auf diese Idee - dass Getter und Setter Funktionen sind und dazu verwendet werden können:

  • validieren
  • Daten ändern
  • duck type (Typ in einen anderen Typ zwingen)

Dies stellt eine smart eine Möglichkeit, Implementierungsdetails und überflüssigen Code wie reguläre Ausdrücke, Type Casts, Try Except-Blöcke, Assertions oder berechnete Werte zu verstecken.

Im Allgemeinen kann CRUD auf einem Objekt oft ziemlich banal sein, aber betrachten Sie das Beispiel von Daten, die in einer relationalen Datenbank persistiert werden sollen. ORMs können Implementierungsdetails bestimmter SQL-Sprachen in den Methoden verstecken, die an fget, fset, fdel gebunden sind, die in einer Eigenschaftsklasse definiert sind, die die schrecklichen if elif else-Leitern verwaltet, die in OO-Code so hässlich sind - und die die einfache und elegante self.variable = something und ersparen dem Entwickler die Arbeit an den Details mit den ORM.

Wenn man Eigenschaften nur als ein tristes Überbleibsel einer Sprache der Knechtschaft und Disziplin (z.B. Java) betrachtet, verfehlt man den Sinn von Deskriptoren.

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