ProgrammazioneSenior Python Developer

Che cosa sono i descrittori in Python? Come vengono implementati, a cosa servono, qual è la loro differenza dalle proprietà e quali sono gli errori tipici nel loro utilizzo?

Supera i colloqui con l'assistente IA Hintsage

Risposta

Un descrittore è un oggetto che implementa uno qualsiasi dei metodi get, set e delete. Consente di controllare l'accesso agli attributi della classe.

Metodi standard del descrittore:

  • __get__(self, instance, owner)
  • __set__(self, instance, value)
  • __delete__(self, instance)

I descrittori possono essere:

  • Classici (descrittori di dati) — implementano set, hanno la priorità sugli attributi normali;
  • Non-classici (descrittori non di dati) — implementano solo get, cedono il passo all'attributo normale.

Esempio di utilizzo:

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('Il valore deve essere >= 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 è solo zucchero sintattico per creare descrittori a livello di classe.

Domanda trabocchetto

Cosa succede se si crea un descrittore come attributo di un'istanza, e non di una classe?

Molti pensano che il descrittore funzionerà, ma non è così.

Risposta corretta:

Il descrittore funziona solo se definito direttamente come attributo della classe. Se si imposta un descrittore come attributo dell'istanza, i metodi get/set non verranno chiamati — si utilizza la logica normale di accesso all'attributo.

Esempi di errori reali dovuti alla mancanza di conoscenza delle sottigliezze dell'argomento


Storia

Utilizzo di property invece di un vero descrittore

Per la validazione e altri attributi correlati, lo sviluppatore utilizzava una semplice property, il che portava a una frequente duplicazione della logica e all'impossibilità di riutilizzare il codice tra diverse classi.


Storia

Non rispetto dell'immutabilità durante il riutilizzo dello storage

Il descrittore memorizzava dati in un attributo interno della propria classe (self.x), e non nello storage interno dell'istanza, portando a un attributo "comune", e diversi istanze della classe sovrascrivevano i valori l'uno dell'altro — i dati "trapelavano" tra oggetti.


Storia

Scambiati descrittore di dati e descrittore non di dati

In una gerarchia complessa è stata tralasciata l'implementazione di set, impedendo così a un attributo normale dell'istanza di sovrascrivere il descrittore, rompendo l'intero meccanismo di validazione — il bug si manifestava non sempre, difficile da debug.