Introspection
Capacità di un linguaggio di determinare tipo e proprietà di un oggetto a runtime (tempo di esecuzione), e presuppone la presenza di forme di type-checking dinamico. Tipica proprietà dei Linguaggi dinamici
Usato principalmente per ispezionare classi, attributi e metodi senza conoscerne il nome (senza che siano hard-coded nel codice)
Utilizzato abbondantemente dalle GUI per autocompletion:
- si recuperano i metodi di un oggetto
- si controlla se sono callable
- vengono mostrati all’utente
Strumenti per introspection
type(obj)
Restituisce la classe dell’oggetto obj. Si può anche reperire con obj.__class__
obj.__class__.__name__
Restituisce il nome della classe dell’oggetto obj.
isinstance(obj, T)
Restituisce True se obj è di classe T, False altrimenti.
(type(obj) == T) == isinstance(obj, T)
ma isinstance(obj, T) == (type(obj) == T) non è detto, dal momento che isinstance(obj, T) ritorna True anche se obj è istanza di una sottoclasse di T.
obj.__dict__
Dizionario che contiene gli attributi modificabili dell’oggetto obj.
dir()/dir(obj)
Senza parametri restituisce i nomi dell’ambiente corrente.
Con i parametri, segue il seguente algoritmo:
- se l’oggetto implementa
__dir__allora viene chiamata quella - se l’oggetto è un modulo, ritorna i nomi del modulo
- se l’oggetto è una classe ritorna gli attributi suoi, e ricorsivamente quelli di tutte le classi base
- per ogni altro oggetto restituisce i suoi attributi, quelli della sua classe e ricorsivamente quelli delle classi base
globals()
Ritorna il namespace globale
locals()
Ritorna il namespace locale
vars()/vars(obj)
Senza parametri equivale a locals().
Con i parametri, se obj ha l’attributo __dict__ ritorna quello, altrimenti solleva un’eccezione TypeError
hasattr(obj, a)
Ritorna un boolean, in base alla presenza di un attributo nominato a all’interno di obj.
getattr(obj, a[, default])
Senza default, ritorna il valore dell’attributo a di obj. Se a non c’è, per evitare un errore, deve essere implementato __getattr__.
Il valore di default viene ritornato solo se a non è presente, e se non è disponibile nemmeno la funzione __getattr__.
class A:
x = 1
def __getattribute__(self,attr):
print("A's __getattribute__ called")
return super().__getattribute__(attr)
def __getattr__(self,attr):
print("A's __getattr also called")
return 'higher priority default value'
class B:
x = 1
def __getattribute__(self,attr):
print("B's __getattribute__ called")
return super().__getattribute__(attr)
a = A()
b = B()
a.x # stampa "A's __getattribute__ called" e ritorna 1
getattr(a, x) # stampa "A's __getattribute__ called" e ritorna 1
a.y # chiama __getattribute__, ma dato che non c'è
# chiama __getattr__, che poi ritorna il valore di default
getattr(a, y) # chiama __getattribute__, ma dato che non c'è
# chiama __getattr__, che poi ritorna il valore di default
b.y # ritorna un errore, perché __getattr__ (fallback)
# non è implementata
id
Ritorna l’id univoco di un oggetto
callable(obj)
Ritorna un valore booleano in base alla possibilità di chiamare quel metodo o meno.
Per modificare struttura e comportamento, c’è un’altra grande proprietà, ovvero la reflection.