Ich mache es wie:
def set_property(property,value):
def get_property(property):
oder
object.property = value
value = object.property
Ich bin neu in Python, daher erforsche ich immer noch die Syntax und möchte einige Ratschläge dazu.
Ich mache es wie:
def set_property(property,value):
def get_property(property):
oder
object.property = value
value = object.property
Ich bin neu in Python, daher erforsche ich immer noch die Syntax und möchte einige Ratschläge dazu.
Antworten:
Versuchen Sie Folgendes: Python-Eigenschaft
Der Beispielcode lautet:
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
print("getter of x called")
return self._x
@x.setter
def x(self, value):
print("setter of x called")
self._x = value
@x.deleter
def x(self):
print("deleter of x called")
del self._x
c = C()
c.x = 'foo' # setter called
foo = c.x # getter called
del c.x # deleter called
._x
(was keine Eigenschaft, sondern nur ein einfaches Attribut ist) umgehen die property
Umhüllung. Nur Referenzen zum .x
Durchgehen der property
.
Was ist die pythonische Art, Getter und Setter zu verwenden?
Die "pythonische" Methode besteht nicht darin, "Getter" und "Setter" zu verwenden, sondern einfache Attribute zu verwenden, wie die Frage zeigt, und sie del
zu löschen (aber die Namen werden geändert, um die unschuldigen ... Eingebauten zu schützen):
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
Wenn Sie später die Einstellung ändern und abrufen möchten, können Sie dies tun, ohne den Benutzercode ändern zu müssen, indem Sie den property
Dekorator verwenden:
class Obj:
"""property demo"""
#
@property # first decorate the getter method
def attribute(self): # This getter method name is *the* name
return self._attribute
#
@attribute.setter # the property decorates with `.setter` now
def attribute(self, value): # name, e.g. "attribute", is the same
self._attribute = value # the "value" name isn't special
#
@attribute.deleter # decorate with `.deleter`
def attribute(self): # again, the method name is the same
del self._attribute
(Jede Dekorationsverwendung kopiert und aktualisiert das vorherige Eigenschaftsobjekt. Beachten Sie daher, dass Sie für jede Funktion / Methode zum Festlegen, Abrufen und Löschen denselben Namen verwenden sollten.
Nachdem Sie das oben Gesagte definiert haben, ist die ursprüngliche Einstellung, das Abrufen und Löschen von Code dieselbe:
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
Sie sollten dies vermeiden:
def set_property(property,value): def get_property(property):
Erstens funktioniert das oben Gesagte nicht, da Sie kein Argument für die Instanz angeben, auf die die Eigenschaft (normalerweise self
) gesetzt werden würde. Dies wäre:
class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
Zweitens dupliziert dies den Zweck von zwei speziellen Methoden, __setattr__
und __getattr__
.
Drittens haben wir auch die setattr
und getattr
eingebauten Funktionen.
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
Der @property
Dekorateur dient zum Erstellen von Gettern und Setzern.
Beispielsweise könnten wir das Einstellungsverhalten ändern, um den festgelegten Wert einzuschränken:
class Protective(object):
@property
def protected_value(self):
return self._protected_value
@protected_value.setter
def protected_value(self, value):
if acceptable(value): # e.g. type or range check
self._protected_value = value
Im Allgemeinen möchten wir die Verwendung vermeiden property
und nur direkte Attribute verwenden.
Dies wird von Benutzern von Python erwartet. Nach der Regel der geringsten Überraschung sollten Sie versuchen, Ihren Benutzern das zu geben, was sie erwarten, es sei denn, Sie haben einen sehr zwingenden Grund für das Gegenteil.
Angenommen, das geschützte Attribut unseres Objekts muss eine Ganzzahl zwischen 0 und einschließlich 100 sein und das Löschen verhindern, mit entsprechenden Meldungen, um den Benutzer über die ordnungsgemäße Verwendung zu informieren:
class Protective(object):
"""protected property demo"""
#
def __init__(self, start_protected_value=0):
self.protected_value = start_protected_value
#
@property
def protected_value(self):
return self._protected_value
#
@protected_value.setter
def protected_value(self, value):
if value != int(value):
raise TypeError("protected_value must be an integer")
if 0 <= value <= 100:
self._protected_value = int(value)
else:
raise ValueError("protected_value must be " +
"between 0 and 100 inclusive")
#
@protected_value.deleter
def protected_value(self):
raise AttributeError("do not delete, protected_value can be set to 0")
(Beachten Sie, dass __init__
sich self.protected_value
die Eigenschaft auf die Eigenschaftsmethoden bezieht self._protected_value
. Auf diese Weise wird __init__
die Eigenschaft über die öffentliche API verwendet, um sicherzustellen, dass sie "geschützt" ist.)
Und Verwendung:
>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0
Ja, das tun sie . .setter
und .deleter
machen Sie Kopien des ursprünglichen Eigentums. Auf diese Weise können Unterklassen das Verhalten ordnungsgemäß ändern, ohne das Verhalten im übergeordneten Element zu ändern.
class Obj:
"""property demo"""
#
@property
def get_only(self):
return self._attribute
#
@get_only.setter
def get_or_set(self, value):
self._attribute = value
#
@get_or_set.deleter
def get_set_or_delete(self):
del self._attribute
Damit dies funktioniert, müssen Sie die entsprechenden Namen verwenden:
obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error
Ich bin nicht sicher, wo dies nützlich wäre, aber der Anwendungsfall ist, wenn Sie eine Nur-Abrufen-, Festlegen- und / oder Löschen-Eigenschaft wünschen. Wahrscheinlich am besten bei semantisch derselben Eigenschaft mit demselben Namen bleiben.
Beginnen Sie mit einfachen Attributen.
Wenn Sie später Funktionen zum Einstellen, Abrufen und Löschen benötigen, können Sie diese mit dem Eigenschaftendekorateur hinzufügen.
Vermeiden Sie Funktionen mit dem Namen set_...
und get_...
- dafür sind Eigenschaften gedacht.
__init__
bezieht sich die Methode auf, self.protected_value
aber der Getter und die Setter beziehen sich auf self._protected_value
. Könnten Sie bitte erklären, wie das funktioniert? Ich habe Ihren Code getestet und er funktioniert wie er ist - dies ist also kein Tippfehler.
__init__
, oder?
self.protected_value = start_protected_value
dass die Setter-Funktion tatsächlich aufgerufen wird. Ich dachte, es wäre eine Aufgabe.
In [1]: class test(object):
def __init__(self):
self.pants = 'pants'
@property
def p(self):
return self.pants
@p.setter
def p(self, value):
self.pants = value * 2
....:
In [2]: t = test()
In [3]: t.p
Out[3]: 'pants'
In [4]: t.p = 10
In [5]: t.p
Out[5]: 20
Mit @property
und können @attribute.setter
Sie nicht nur die "pythonische" Methode verwenden, sondern auch die Gültigkeit von Attributen sowohl beim Erstellen des Objekts als auch beim Ändern überprüfen.
class Person(object):
def __init__(self, p_name=None):
self.name = p_name
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if type(new_name) == str: #type checking for name property
self._name = new_name
else:
raise Exception("Invalid value for name")
Auf diese Weise "verbergen" Sie das _name
Attribut tatsächlich vor Client-Entwicklern und führen auch Überprüfungen des Namenseigenschaftstyps durch. Beachten Sie, dass der Setter aufgerufen wird, wenn Sie diesen Ansatz auch während der Initiierung befolgen. Damit:
p = Person(12)
Wird dazu führen:
Exception: Invalid value for name
Aber:
>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception
Schauen Sie sich den @property
Dekorateur an .
Sie können Accessoren / Mutatoren (dh @attr.setter
und @property
) verwenden oder nicht, aber das Wichtigste ist , konsistent zu sein!
Wenn Sie verwenden, @property
um einfach auf ein Attribut zuzugreifen, z
class myClass:
def __init__(a):
self._a = a
@property
def a(self):
return self._a
Verwenden Sie es, um auf jedes * Attribut zuzugreifen ! Es wäre eine schlechte Praxis, auf einige Attribute zuzugreifen @property
und einige andere Eigenschaften ohne einen Accessor öffentlich zu lassen (dh Namen ohne Unterstrich), z. B. nicht
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@property
def a(self):
return self.a
Beachten Sie, dass self.b
hier kein expliziter Accessor vorhanden ist, obwohl er öffentlich ist.
Ähnlich ist es mit Setter (oder Mutatoren ), fühlen Sie sich frei zu verwenden , @attribute.setter
aber konsequent sein! Wenn Sie z
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@a.setter
def a(self, value):
return self.a = value
Es fällt mir schwer, Ihre Absicht zu erraten. Einerseits sagen Sie, dass beide a
und b
öffentlich sind (kein führender Unterstrich in ihren Namen), so dass ich theoretisch auf beide zugreifen / mutieren (get / set) dürfen sollte. Aber dann geben Sie einen expliziten Mutator nur für an a
, der mir sagt, dass ich vielleicht nicht in der Lage sein sollte, zu setzen b
. Da Sie einen expliziten Mutator angegeben haben, bin ich mir nicht sicher, ob das Fehlen von explizitem accessor ( @property
) bedeutet, dass ich nicht auf eine dieser Variablen zugreifen kann oder dass Sie nur sparsam damit umgehen @property
.
* Die Ausnahme ist, wenn Sie einige Variablen explizit zugänglich oder veränderbar machen möchten, aber nicht beide, oder wenn Sie beim Zugriff auf oder bei der Mutation eines Attributs eine zusätzliche Logik ausführen möchten. Dies ist, wenn ich persönlich benutze @property
und @attribute.setter
(ansonsten keine expliziten Acessoren / Mutatoren für öffentliche Attribute).
Zuletzt Vorschläge für PEP8 und Google Style Guide:
PEP8, Designing for Inheritance, sagt:
Für einfache öffentliche Datenattribute ist es am besten, nur den Attributnamen ohne komplizierte Accessor / Mutator-Methoden verfügbar zu machen . Beachten Sie, dass Python einen einfachen Weg zur zukünftigen Verbesserung bietet, falls Sie feststellen, dass ein einfaches Datenattribut das Funktionsverhalten verbessern muss. Verwenden Sie in diesem Fall Eigenschaften, um die funktionale Implementierung hinter der einfachen Datenattributzugriffssyntax zu verbergen.
Auf der anderen Seite lautet die Empfehlung gemäß den Python-Sprachregeln / -eigenschaften des Google Style Guide :
Verwenden Sie Eigenschaften in neuem Code, um auf Daten zuzugreifen oder diese festzulegen, für die Sie normalerweise einfache, einfache Accessor- oder Setter-Methoden verwendet hätten. Eigenschaften sollten mit dem
@property
Dekorateur erstellt werden.
Die Vorteile dieses Ansatzes:
Die Lesbarkeit wird verbessert, indem explizite get- und set-Methodenaufrufe für einen einfachen Attributzugriff entfallen. Ermöglicht faulen Berechnungen. Betrachtet die pythonische Methode zur Pflege der Schnittstelle einer Klasse. In Bezug auf die Leistung umgeht das Zulassen von Eigenschaften das Erfordernis trivialer Zugriffsmethoden, wenn ein direkter Variablenzugriff sinnvoll ist. Auf diese Weise können auch in Zukunft Zugriffsmethoden hinzugefügt werden, ohne die Schnittstelle zu beschädigen.
und Nachteile:
Muss von
object
Python 2 erben . Kann Nebenwirkungen verbergen, ähnlich wie das Überladen von Operatoren. Kann für Unterklassen verwirrend sein.
@property
, @property
scheint es eine schlechte Entscheidung zu sein , den Rest auch zu verwenden .
@property
(z. B. Ausführen einer speziellen Logik, bevor Sie ein Attribut zurückgeben). Warum würden Sie sonst ein Attribut mit @propery
und nicht mit anderen dekorieren ?
@property
, oder? Wenn Ihr Getter ist return this._x
und Ihr Setter ist this._x = new_x
, dann ist die Verwendung @property
überhaupt etwas albern.
@property
die Konsistenz ist".
Sie können die magischen Methoden __getattribute__
und verwenden __setattr__
.
class MyClass:
def __init__(self, attrvalue):
self.myattr = attrvalue
def __getattribute__(self, attr):
if attr == "myattr":
#Getter for myattr
def __setattr__(self, attr):
if attr == "myattr":
#Setter for myattr
Sei dir dessen bewusst __getattr__
und __getattribute__
bin nicht dasselbe. __getattr__
wird nur aufgerufen, wenn das Attribut nicht gefunden wird.