property: obliczane atrybuty
@property pozwala wywołać metodę tak, jakby była regularnym atrybutem, z kontrolą przy przypisaniu i bez zmiany składni z zewnątrz.
property: write-on-the-job, bez zmiany składni odczytu
Dekorator @property pozwala wywoływać metodę TAK SAMO,
jak zwykły atrybut — bez nawiasów:
class Krug:
def __init__(self, radius):
self._radius = radius
@property
def ploshchad(self):
return 3.14159 * self._radius ** 2
krug = Krug(10)
print(krug.ploshchad) #314.159 – bez nawiasów, choć to metoda!
ploshchad nigdzie się nie przechowuje — jest obliczana na nowo przy każdorazowym wywołaniu. Na zewnątrz nie da się odróżnić tego od zwykłego atrybutu.
@x.setter: Sprawdzenie przydziału
class Krug:
def __init__(self, radius):
self.radius = radius # przechodzi przez setter poniżej
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError(„promień musi być dodatni”)
self._radius = value
krug = Krug(10)
krug.radius = 5 # jest testowane
krug.radius = -1 #ValueError - Zadanie odrzucone
radius jest stałą cechą, a jutro musisz ją sprawdzić, możesz przerobić na property i cały kod, który napisał krug.radius = 5, nadal będzie działać bez żadnej poprawki. Dlatego w Python zwykle zaczyna się od zwykłych atrybutów i dodaje property dopiero wtedy, gdy sprawdzenie rzeczywiście jest potrzebne — a nie owija w property wszystkiego od samego początku.class Krug:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
def radius(self, value): # zapomniałem @radius.setter z góry!
self._radius = value
krug = Krug(10)
krug.radius = 5
>>> krug.radius = 5
>>> print(krug.radius)
5
Nie będzie pomyłki – a to jest po prostu najbardziej podstępna rzecz. Bez dekoratora @radius.setter druga funkcja radius jest osobna, regularna metoda, która po prostu ZASTĄPIŁA property z góry (ta sama nazwa w grupie!). Po tym, Krug.radius nie jest już property, lecz funkcją zwykłą, oraz krug.radius = 5 po prostu tworzy atrybut INSTANCE radius, cicho zasłaniając zarówno metodę, jak i cały czek z §14.11 — numer jest wydrukowany 5, a nie wynik getter'a.
@radius.setter
def radius(self, value):
self._radius = value