2.33. Introspezione¶
Una manciata di funzioni integrate permette a un programma in esecuzione di ispezionare sé stesso – i valori con cui sta lavorando, i namespace in cui questi valori risiedono e le relazioni tra le classi. Ricorri ad esse quando devi prendere decisioni basate su ciò che un oggetto effettivamente è, anziché su ciò che il suo chiamante afferma che sia.
2.33.1. Identità e hashing¶
id()– un intero univoco che identifica un oggetto per tutto il tempo in cui è vivo. Due nomi che fanno riferimento allo stesso oggetto restituiscono lo stesso id; due oggetti uguali ma distinti no.
>>> a = [1, 2, 3]
>>> b = a
>>> c = [1, 2, 3]
>>> id(a) == id(b) # same list
True
>>> id(a) == id(c) # equal but distinct
False
L’id non è portabile tra le esecuzioni e non ha significato oltre a «stesso oggetto o oggetto diverso».
hash()– il valore hash di un oggetto, lo stesso numero chedictesetusano per cercarlo. Due oggetti uguali producono lo stesso valore hash; funzionano solo i tipi hashabili (valori immutabili, per lo più).
>>> hash("abc") # some integer, build-dependent
-1600925533
>>> hash([1, 2])
TypeError: unhashable type: 'list'
2.33.2. Interrogare tipi e chiamabilità¶
type()– la classe esatta di un valore.type(x) is intchiede «x è esattamente un int» (nessuna sottoclasse);isinstance()è di solito ciò che desideri invece.isinstance()– «x è un’istanza di questa classe, o di una sua sottoclasse?» Lo strumento standard per il dispatch basato sul tipo all’interno delle funzioni.issubclass()– la controparte a livello di classe. Accetta due classi anziché un’istanza.callable()–Truese l’argomento può essere chiamato con(). Utile quando ricevi un argomento che potrebbe essere una funzione o un valore semplice.
>>> isinstance(3, int)
True
>>> isinstance(True, int) # bool is a subclass of int
True
>>> issubclass(bool, int)
True
>>> callable(len)
True
>>> callable(10)
False
Un pattern che usa callable:
def call_or_return(x):
return x() if callable(x) else x
2.33.3. Esaminare gli scope¶
globals()– il namespace globale del modulo comedict. La lettura da esso funziona; la scrittura è reale, ma farlo al di fuori dell’esplorazione al REPL rende un programma difficile da seguire.locals()– il namespace locale nel punto di chiamata. All’interno di una funzione riflette le variabili locali; la modifica del dict restituito non è garantita scrivere effettivamente nelle variabili locali (comportamento dipendente dall’implementazione).
name = "OpenMV"
def f():
x = 10
print(globals()["name"]) # OpenMV
print(locals()) # {'x': 10}
Questi due sono utili per il debug e per gli strumenti che hanno bisogno di scoprire cosa è definito. Ricorri ad essi con parsimonia nel codice ordinario – una funzione che modifica globals() è una delle cose più difficili su cui ragionare in Python.
2.33.4. Vedi anche¶
dir() e help(), trattati in debugging, sono gli strumenti di introspezione quotidiani per esplorare la superficie di un oggetto sconosciuto.