Deskryptory: __get__, __set__, __delete__
Proszę implementować protokół deskryptora do kontrolowania dostępu do atrybutów.
Deskryptory: __get__, __set__, __delete__ to bezpłatna lekcja Python Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Python Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Python Academy zawiera 4 lekcji w sumie.
Czym jest deskryptor?
Deskryptor to obiekt definiujący __get__, __set__ lub __delete__, przypisany jako atrybut klasy. Python wywołuje te metody podczas dostępu do atrybutu.
class Descriptor:
def __get__(self, obj, objtype=None):
print(f"__get__ called, obj={obj}")
return 42
class MyClass:
attr = Descriptor()
print(MyClass().attr) # __get__ called ... 42Deskryptory danych a deskryptory niedanych
Deskryptory danych definiują __set__ lub __delete__ i mają pierwszeństwo przed __dict__ instancji. Deskryptory niedanych, definiujące tylko __get__, ustępują miejsca __dict__.
class DataDesc:
def __get__(self, obj, t): return "data"
def __set__(self, obj, val): pass
class NonDataDesc:
def __get__(self, obj, t): return "nondata"
class C:
d = DataDesc()
n = NonDataDesc()
c = C()
c.__dict__["n"] = "instance" # shadows the non-data desc
c.__dict__["d"] = "instance" # does NOT shadow data desc
print(c.n) # instance
print(c.d) # dataSygnatura __get__
__get__(self, obj, objtype): obj jest instancją lub ma wartość None, gdy atrybut jest pobierany za pośrednictwem klasy; objtype jest klasą.
class Verbose:
def __get__(self, obj, objtype=None):
if obj is None:
return self # accessed on class
return f"value for {obj!r}"
class Widget:
label = Verbose()
print(Widget.label) # <Verbose object>
print(Widget().label) # value for <Widget object>__set__ i walidacja
Proszę zaimplementować __set__(self, obj, value), aby zweryfikować lub przekształcić wartość przed jej zapisaniem.
class PositiveInt:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, t):
return obj.__dict__.get(self.name)
def __set__(self, obj, val):
if not isinstance(val, int) or val <= 0:
raise ValueError(f"{self.name} must be a positive int")
obj.__dict__[self.name] = val
class Rect:
width = PositiveInt()
height = PositiveInt()
r = Rect()
r.width = 10
# r.width = -1 # ValueError__set_name__
Python 3.6+ wywołuje __set_name__(owner, name) na deskryptorze, gdy jest on przypisywany do klasy, dzięki czemu deskryptor otrzymuje dostęp do własnej nazwy atrybutu.
class Typed:
def __set_name__(self, owner, name):
self.public = name
self.private = "_" + name
def __get__(self, obj, t):
return None if obj is None else getattr(obj, self.private, None)
def __set__(self, obj, val):
setattr(obj, self.private, val)
class User:
name = Typed()
u = User()
u.name = "Alice"
print(u.name) # Alice__delete__ do usuwania atrybutów
Proszę zdefiniować __delete__(self, obj), aby przechwytywać operację del obj.attr.
class Protected:
def __set_name__(self, owner, name): self.name = name
def __get__(self, obj, t):
return obj.__dict__.get(self.name)
def __set__(self, obj, v): obj.__dict__[self.name] = v
def __delete__(self, obj):
raise AttributeError(f"Cannot delete {self.name}")
class Config:
host = Protected()
c = Config(); c.host = "localhost"
# del c.host # AttributeErrorFunkcje są deskryptorami niedanych
Funkcje implementują __get__, aby po pobraniu z instancji zwracać metodę związaną. Tak właśnie działa wiązanie metod w Pythonie.
def greet(self):
return f"Hello from {self}"
class C: pass
C.greet = greet
c = C()
print(c.greet()) # Hello from <C object>classmethod i staticmethod są deskryptorami
classmethod i staticmethod to wbudowane klasy deskryptorów, które opakowują funkcje i zmieniają wartość zwracaną przez __get__.
class MyClass:
@classmethod
def from_string(cls, s):
return cls()
# classmethod.__get__ returns a bound method with cls
# staticmethod.__get__ returns the plain functionDeskryptor leniwego atrybutu
Proszę zbudować deskryptor, który oblicza wartość raz i buforuje ją dla każdej instancji.
class LazyAttr:
def __init__(self, func): self.func = func
def __set_name__(self, owner, name): self.name = name
def __get__(self, obj, t):
if obj is None: return self
val = self.func(obj)
obj.__dict__[self.name] = val # shadows descriptor
return val
class Report:
@LazyAttr
def summary(self):
print("computing...")
return "done"
r = Report()
print(r.summary) # computing... done
print(r.summary) # done (cached)property jest deskryptorem
property samo jest deskryptorem danych zaimplementowanym w języku C. Zrozumienie deskryptorów pomaga zrozumieć działanie @property, @classmethod i @staticmethod.
class Circle:
def __init__(self, r): self._r = r
@property
def radius(self): return self._r
@radius.setter
def radius(self, v):
if v < 0: raise ValueError
self._r = v
# property is: property.__get__ = fget, property.__set__ = fsetKolejność wyszukiwania deskryptorów
Kolejność wyszukiwania atrybutów w Pythonie: 1) deskryptory danych z typu (MRO), 2) __dict__ instancji, 3) deskryptory niedanych i zmienne klasowe.
# Pseudocode for obj.attr:
# 1. Check type(obj).__mro__ for a data descriptor
# 2. Check obj.__dict__
# 3. Check type(obj).__mro__ for non-data descriptor or class var
# 4. Raise AttributeErrorSzybkie sprawdzenie
Jaki typ deskryptora ma pierwszeństwo przed __dict__ instancji?
Podsumowanie
Deskryptory przechwytują dostęp do atrybutów za pomocą __get__, __set__, __delete__. Deskryptory danych przesłaniają __dict__ instancji, a deskryptory niedanych tego nie robią. Należy użyć __set_name__, aby poznać nazwę atrybutu. property, classmethod i staticmethod są deskryptorami.
Często zadawane pytania
Czy lekcja „Deskryptory: __get__, __set__, __delete__” jest bezpłatna?
Tak — pełny tekst „Deskryptory: __get__, __set__, __delete__” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Python Academy, przejdź na CoddyKit PRO. Kurs Python Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Deskryptory: __get__, __set__, __delete__”?
Proszę implementować protokół deskryptora do kontrolowania dostępu do atrybutów. Ćwiczysz Python Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Python Academy?
Nie wymagamy żadnego doświadczenia. Python Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Deskryptory: __get__, __set__, __delete__”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Python Academy?
Tak. Każda lekcja Python Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Jak tworzone są klasy Pythona
- Pisanie własnych metaklas
- Deskryptory: __get__, __set__, __delete__
- __slots__ i optymalizacja pamięci