Historia pytania:
W klasycznym OOP enkapsulacja realizowana jest poprzez prywatne pola oraz gettery/settery, co jest nieporęczne i "nienasłonikowe". W Pythonie od wersji 2.2 pojawił się dekorator @property, który umożliwia dostęp do metod-getterów i setterów jak do zwykłych atrybutów, realizując poprawną enkapsulację z wygodną składnią.
Problem:
Bez dekoratorów property musiałbyś wyraźnie definiować metody dostępu i ustawienia wartości (na przykład get_x() i set_x(val)), co sprawia, że kod jest mniej czytelny, a użytkownicy klasy nie są chronieni przed bezpośrednim dostępem do wewnętrznych danych. Pojawiają się problemy przy refaktoryzacji oraz zmianie wewnętrznej logiki przechowywania lub obliczania wartości.
Rozwiązanie:
Dekorator @property pozwala definiować gettery, settery i deletery za pomocą jednego synatksu. Wygląda to zwięźle, wygodnie, enkapsuluje detale implementacji i umożliwia transparentną zmianę sposobu obliczania właściwości bez naruszania interfejsu klasy.
Przykład kodu:
class Temperature: def __init__(self, celsius): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("Temperatura poniżej -273.15°C nie jest możliwa!") self._celsius = value
Kluczowe cechy:
@property pozwala na dostęp do metod jak do zwykłych atrybutów.Czy można zrobić właściwość tylko do odczytu, ale nie do zapisu/usunięcia?
Tak, jeśli zdefiniujesz tylko metodę z dekoratorem @property bez settera i deletera, właściwość będzie dostępna tylko do odczytu.
class Sample: @property def value(self): return 42
Co się stanie, jeśli nazwa właściwości pokrywa się z prywatnym atrybutem?
Zazwyczaj property używana jest jako "warstwa" do prywatnego atrybutu, którego nazwa zaczyna się od podkreślenia (na przykład _x). Takiego pokrywania należy unikać, bo może spowodować rekursywne wywołanie:
class Bad: @property def x(self): return self.x # Nieskończona rekursja
Czy można przypisać property tylko dla klasy?
Nie, standardowy @property działa na instancjach klasy. Aby stworzyć class property, należałoby używać wzorców zewnętrznych lub specjalnych bibliotek (@classmethod razem z property nie działają bezpośrednio).
Programista bezpośrednio odwołuje się do pola klasy (self.celsius), a nie przez property. Później dodawana jest walidacja, ale konsumenci klasy nadal mogą bezpośrednio modyfikować prywatny atrybut i omijać kontrole.
Zalety:
Prosto i szybko pracować, gdy nie ma skomplikowanej logiki.
Wady:
Naruszona enkapsulacja, łatwo uzyskać niepoprawny stan obiektu, pojawia się zamieszanie.
Użycie property i ukrycie wewnętrznego atrybutu przez _celsius. Walidacja, caching i logika centralizują się w property.
Zalety:
Kod zabezpieczony, interfejs stabilny — można zmieniać implementację właściwości bez wpływu na użytkowników klasy.
Wady:
W przypadku dużych i skomplikowanych obiektów można zwiększyć złożoność debugowania, jeśli przesadzi się z property.