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:

  1. si recuperano i metodi di un oggetto
  2. si controlla se sono callable
  3. 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:

  1. se l’oggetto implementa __dir__ allora viene chiamata quella
  2. se l’oggetto è un modulo, ritorna i nomi del modulo
  3. se l’oggetto è una classe ritorna gli attributi suoi, e ricorsivamente quelli di tutte le classi base
  4. 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.

python_book6