Deskryptor to obiekt, który implementuje dowolną z metod get, set i delete. Pozwala on kontrolować dostęp do atrybutów klasy.
Standardowe metody deskryptora:
__get__(self, instance, owner)__set__(self, instance, value)__delete__(self, instance)Deskryptory dzielą się na:
Przykład użycia:
class OnlyPositive: def __init__(self): self._name = '_value' def __get__(self, instance, owner): return instance.__dict__[self._name] def __set__(self, instance, value): if value < 0: raise ValueError('Wartość musi być >= 0') instance.__dict__[self._name] = value class Account: value = OnlyPositive() def __init__(self, value): self.value = value acc = Account(10) acc.value = -1 # ValueError!
property to tylko syntaktyczny cukier do tworzenia deskryptorów na poziomie klasy.
Co się stanie, jeśli utworzymy deskryptor jako atrybut instancji, a nie klasy?
Wielu sądzi, że deskryptor będzie działać, ale to nieprawda.
Prawidłowa odpowiedź:
Deskryptor działa tylko, jeśli jest bezpośrednio zdefiniowany jako atrybut klasy. Jeśli deskryptor jest zdefiniowany jako atrybut instancji, metody get/set nie zostaną wywołane — działa zwykła logika dostępu do atrybutu.
Historia
Użycie property zamiast pełnoprawnego deskryptora
Do walidacji i innych powiązanych atrybutów programista użył prostego property, co prowadziło do częstego powielania logiki i braku możliwości ponownego wykorzystania kodu pomiędzy różnymi klasami.
Historia
Niezachowanie niezmienności przy ponownym użyciu magazynowania
Deskryptor przechowywał dane w wewnętrznym atrybucie swojej klasy (self.x), a nie w wewnętrznym magazynie instancji, przez co atrybut stawał się "wspólny" i różne instancje klasy nadpisywały sobie nawzajem wartości — "przeciekały" dane między obiektami.
Historia
Pomyłka między deskryptorem danych a deskryptorem nie-danych
W skomplikowanej hierarchii pominięto implementację set, przez co zwykły atrybut instancji przysłonił deskryptor, łamiąc cały mechanizm walidacji — błąd występował nie zawsze, był trudny do debugowania.