Rozdział 14 · Tworzenie rzeczywistych obiektów

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:

property_getter.py
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

property_setter.py
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
Główna korzyść property: kod zewnętrzny pozostaje niezmieniony
Jeśli dziś 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.
Debug Lab 6: Zapomniałem o @radius.setter dekoratorze
bez_setter.py
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
Co widać na ekranie

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.

Poprawiony kod
s_setter.py
    @radius.setter
    def radius(self, value):
        self._radius = value
Praktyka: property
interaktywny laptop bezpośrednio w przeglądarce – Python 3.14 przez Pyodide, bez instalacji
Otwórz praktykę →