Robię to tak:
def set_property(property,value):
def get_property(property):
lub
object.property = value
value = object.property
Jestem nowy w Pythonie, więc wciąż badam składnię i chciałbym uzyskać porady na ten temat.
Robię to tak:
def set_property(property,value):
def get_property(property):
lub
object.property = value
value = object.property
Jestem nowy w Pythonie, więc wciąż badam składnię i chciałbym uzyskać porady na ten temat.
Odpowiedzi:
Spróbuj tego: Właściwość Python
Przykładowy kod to:
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
(które nie są własnością, tylko zwykłym atrybutem) omijają property
zawijanie. Tylko odniesienia do .x
przejścia przez property
.
Jaki jest pythonowy sposób używania pobierających i ustawiających?
„Pythońskim” sposobem nie jest używanie „pobierających” i „ustawiających”, ale używanie prostych atrybutów, jak pokazuje pytanie, i del
do usuwania (ale nazwy są zmieniane, aby chronić niewinne ... wbudowane):
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
Jeśli później chcesz zmodyfikować ustawienie i uzyskać, możesz to zrobić bez konieczności zmiany kodu użytkownika, używając property
dekoratora:
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
(Każde użycie dekoratora kopiuje i aktualizuje poprzedni obiekt właściwości, dlatego należy pamiętać, że należy używać tej samej nazwy dla każdego zestawu, funkcji pobierania i usuwania funkcji / metody.
Po zdefiniowaniu powyższego oryginalne ustawienie, pobieranie i usuwanie kodu jest takie samo:
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
Powinieneś tego unikać:
def set_property(property,value): def get_property(property):
Po pierwsze powyższe nie działa, ponieważ nie podajesz argumentu dla instancji, dla której właściwość byłaby ustawiona na (zwykle self
), która byłaby:
class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
Po drugie, powiela to cel dwóch specjalnych metod __setattr__
oraz __getattr__
.
Po trzecie, mamy też setattr
i getattr
wbudowanych funkcji.
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
@property
Dekorator jest do tworzenia pobierające i ustawiające.
Na przykład, moglibyśmy zmodyfikować zachowanie ustawienia, aby nałożyć ograniczenia na ustawianą wartość:
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
Ogólnie rzecz biorąc, chcemy unikać używania property
i po prostu używać atrybutów bezpośrednich.
Tego oczekują użytkownicy Pythona. Zgodnie z zasadą najmniejszego zaskoczenia powinieneś spróbować dać użytkownikom to, czego oczekują, chyba że masz bardzo ważny powód, aby postąpić inaczej.
Powiedzmy na przykład, że potrzebowaliśmy, aby atrybut chroniony naszego obiektu był liczbą całkowitą od 0 do 100 włącznie i zapobiegał jego usunięciu, z odpowiednimi komunikatami informującymi użytkownika o jego właściwym użyciu:
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")
(Należy zauważyć, że __init__
odnosi się do, self.protected_value
ale odnoszą się do niego metody własności self._protected_value
. Ma to na celu __init__
wykorzystanie właściwości za pośrednictwem publicznego interfejsu API, zapewniając jej „ochronę”).
I użycie:
>>> 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
Tak, robią . .setter
i .deleter
wykonaj kopie oryginalnej nieruchomości. Pozwala to podklasom odpowiednio modyfikować zachowanie bez zmiany zachowania w obiekcie nadrzędnym.
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
Teraz, aby to zadziałało, musisz użyć odpowiednich nazw:
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
Nie jestem pewien, gdzie byłoby to przydatne, ale przypadek użycia dotyczy sytuacji, gdy potrzebujesz właściwości get, set i / lub delete-only. Prawdopodobnie najlepiej trzymać się semantycznie tej samej własności o tej samej nazwie.
Zacznij od prostych atrybutów.
Jeśli później potrzebujesz funkcji związanej z ustawieniem, uzyskiwaniem i usuwaniem, możesz dodać ją za pomocą dekoratora właściwości.
Unikaj nazwanych funkcji set_...
i get_...
- po to są właściwości.
__init__
metoda odnosi się, self.protected_value
ale do gettera i settera self._protected_value
. Czy możesz wyjaśnić, jak to działa? Przetestowałem twój kod i działa tak, jak jest - więc to nie jest literówka.
__init__
tego?
self.protected_value = start_protected_value
tak naprawdę wywołuje funkcję setera; Myślałem, że to zadanie.
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
Używanie @property
i @attribute.setter
pomaga nie tylko używać „pythonowego” sposobu, ale także sprawdzać poprawność atrybutów zarówno podczas tworzenia obiektu, jak i podczas jego zmiany.
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")
Dzięki temu faktycznie „ukrywasz” _name
atrybut przed programistami klientów, a także sprawdzasz typ właściwości nazwy. Zauważ, że postępując zgodnie z tym podejściem, nawet podczas inicjacji, setter zostaje wywołany. Więc:
p = Person(12)
Zaprowadzi do:
Exception: Invalid value for name
Ale:
>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception
Sprawdź @property
dekorator .
Możesz używać akcesoriów / mutatorów (tj. @attr.setter
I @property
) lub nie, ale najważniejsze jest zachowanie spójności!
Jeśli używasz @property
po prostu uzyskać dostęp do atrybutu, np
class myClass:
def __init__(a):
self._a = a
@property
def a(self):
return self._a
użyj go, aby uzyskać dostęp do każdego * atrybutu! Złą praktyką byłoby uzyskiwanie dostępu do niektórych atrybutów za pomocą @property
i pozostawienie niektórych innych właściwości publicznych (np. Nazwa bez podkreślenia) bez akcesorium, np. Nie rób
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@property
def a(self):
return self.a
Pamiętaj, że self.b
nie ma tutaj wyraźnego akcesorium, nawet jeśli jest publiczne.
Podobnie z seterami (lub mutatorami ), nie krępuj się używać, @attribute.setter
ale bądź konsekwentny! Kiedy robisz np
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@a.setter
def a(self, value):
return self.a = value
Trudno mi odgadnąć twoją intencję. Z jednej strony mówisz, że oba a
i b
są publiczne (brak wiodących znaków podkreślenia w ich nazwach), więc teoretycznie powinienem mieć dostęp do / mutowania (get / set) obu. Ale wtedy podajesz tylko jawny mutator dla a
, który mówi mi, że może nie powinienem być w stanie ustawić b
. Ponieważ podałeś wyraźny mutator, nie jestem pewien, czy brak wyraźnego accessor ( @property
) oznacza, że nie powinienem mieć dostępu do żadnej z tych zmiennych, czy po prostu byłeś oszczędny w użyciu @property
.
* Wyjątkiem jest sytuacja, gdy jawnie chcesz, aby niektóre zmienne były dostępne lub zmienne, ale nie jedno lub drugie, lub chcesz wykonać dodatkową logikę podczas uzyskiwania dostępu lub modyfikowania atrybutu. To jest, kiedy osobiście używam @property
i @attribute.setter
(w przeciwnym razie nie ma wyraźnych acessorów / mutatorów dla publicznych atrybutów).
Wreszcie sugestie PEP8 i Przewodnika po stylu Google:
PEP8, Designing for Inheritance mówi:
W przypadku prostych publicznych atrybutów danych najlepiej jest ujawnić tylko nazwę atrybutu, bez skomplikowanych metod dostępu / mutatora . Należy pamiętać, że Python zapewnia łatwą ścieżkę do przyszłego rozszerzenia, jeśli okaże się, że prosty atrybut danych musi zwiększyć funkcjonalne zachowanie. W takim przypadku użyj właściwości, aby ukryć funkcjonalną implementację kryjącą się za prostą składnią dostępu do atrybutów danych.
Z drugiej strony, zgodnie z Google Style Guide Python Language Rules / Properties zaleca się:
Użyj właściwości w nowym kodzie, aby uzyskać dostęp do danych lub ustawić je tam, gdzie normalnie użyłbyś prostych, lekkich metod akcesorów lub ustawiaczy. Właściwości należy utworzyć za pomocą
@property
dekoratora.
Zalety tego podejścia:
Czytelność jest zwiększona poprzez wyeliminowanie jawnych wywołań metod get i set dla łatwego dostępu do atrybutów. Pozwala lenić obliczenia. Uważany za Pythoniczny sposób utrzymania interfejsu klasy. Jeśli chodzi o wydajność, zezwalanie właściwościom na omijanie wymagających prostych metod dostępu, gdy bezpośredni dostęp do zmiennych jest uzasadniony. Pozwala to również na dodanie metod akcesorów w przyszłości bez zrywania interfejsu.
i minusy:
Musi dziedziczyć
object
po Pythonie 2. Może ukrywać skutki uboczne, podobnie jak przeciążenie operatora. Może być mylące dla podklas.
@property
, to użycie reszty również @property
wydaje się złą decyzją.
@property
(np. Wykonanie specjalnej logiki przed zwróceniem atrybutu). W przeciwnym razie dlaczego miałbyś ozdobić jeden atrybut @propery
innym, a nie innym?
@property
na początku, prawda? Jeśli twój getter jest, return this._x
a twój seter jest this._x = new_x
, to używanie @property
w ogóle jest trochę głupie.
@property
jest konsekwencja”.
Możesz użyć magicznych metod __getattribute__
i __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
Pamiętaj o tym __getattr__
i __getattribute__
nie są takie same. __getattr__
jest wywoływane tylko wtedy, gdy atrybut nie zostanie znaleziony.