Descriptor: __get__, __set__, __delete__
Implementi il protocollo dei descriptor per controllare l'accesso agli attributi.
Descriptor: __get__, __set__, __delete__ è una lezione Python Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Python Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Python Academy include 4 lezioni in totale.
Che cos'è un descriptor
Un descriptor è un oggetto che definisce __get__, __set__ o __delete__ e viene assegnato come attributo di classe. Python chiama questi metodi quando si accede all'attributo.
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 ... 42Descriptor di dati e non di dati
I descriptor di dati definiscono __set__ o __delete__ e hanno precedenza sul __dict__ dell'istanza. I descriptor non di dati, che definiscono solo __get__, cedono la precedenza a __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) # dataFirma di __get__
__get__(self, obj, objtype): obj è l'istanza, oppure None quando si accede alla classe; objtype è la classe.
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__ e validazione
Implementi __set__(self, obj, value) per validare o trasformare un valore prima di memorizzarlo.
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__
A partire da Python 3.6, Python chiama __set_name__(owner, name) sul descriptor quando questo viene assegnato a una classe, fornendogli accesso al nome del proprio attributo.
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__ per la rimozione degli attributi
Definisca __delete__(self, obj) per intercettare 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 # AttributeErrorLe funzioni sono descriptor non di dati
Le funzioni implementano __get__ per restituire un metodo associato quando vengono utilizzate su un'istanza. È così che funziona l'associazione dei metodi in Python.
def greet(self):
return f"Hello from {self}"
class C: pass
C.greet = greet
c = C()
print(c.greet()) # Hello from <C object>classmethod e staticmethod sono descriptor
classmethod e staticmethod sono classi descriptor integrate che racchiudono funzioni e modificano il valore restituito da __get__.
class MyClass:
@classmethod
def from_string(cls, s):
return cls()
# classmethod.__get__ returns a bound method with cls
# staticmethod.__get__ returns the plain functionDescriptor di attributi lazy
Crei un descriptor che calcola un valore una sola volta e lo memorizza nella cache per ogni istanza.
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 è un descriptor
property è a sua volta un descriptor di dati implementato in C. Comprendere i descriptor aiuta a capire come funzionano @property, @classmethod e @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__ = fsetOrdine di ricerca dei descrittori
Ordine di ricerca degli attributi in Python: 1) descrittori di dati dal tipo (MRO), 2) __dict__ dell'istanza, 3) descrittori non di dati e variabili di classe.
# 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 AttributeErrorVerifica rapida
Quale tipo di descrittore ha la precedenza sul __dict__ dell'istanza?
Riepilogo
I descrittori intercettano l'accesso agli attributi tramite __get__, __set__, __delete__. I descrittori di dati hanno la precedenza sul __dict__ dell'istanza, mentre quelli non di dati no. Utilizzi __set_name__ per conoscere il nome dell'attributo. property, classmethod e staticmethod sono tutti descrittori.
Domande Frequenti
La lezione «Descriptor: __get__, __set__, __delete__» è gratuita?
Sì — il testo completo di «Descriptor: __get__, __set__, __delete__» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Python Academy, passa a CoddyKit PRO. Il corso Python Academy include 4 lezioni in totale.
Cosa imparerò in «Descriptor: __get__, __set__, __delete__»?
Implementi il protocollo dei descriptor per controllare l'accesso agli attributi. Eserciti Python Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Python Academy?
Non è richiesta alcuna esperienza precedente. Python Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «Descriptor: __get__, __set__, __delete__»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Python Academy?
Sì. Ogni lezione Python Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Come vengono create le classi Python
- Scrittura di metaclassi personalizzate
- Descriptor: __get__, __set__, __delete__
- __slots__ e ottimizzazione della memoria