back to top

Moduli Python: directory, namespace e variabili d’ambiente

Per utilizzare al meglio i moduli è fondamentale capire come essi vengono gestiti da Python, approfondire questo argomento consente di scoprire l’utilità di alcune funzioni native del linguaggio che possono rivelarsi fondamentali per migliorare la produttività in sede di sviluppo, risparmiare sulla quantità di codice digitato e ottimizzare le prestazioni delle nostre applicazioni.

Il meccanismo di importazione dei moduli

Abbiamo detto che un modulo è sostanzialmente un file contenente del codice, ma in che modo Python riesce ad individuare un modulo al momento dell’importazione? La keyword "import" inizializza un ricerca basata sul nome del modulo desiderato, tale ricerca viene effettuata innanzitutto a carico dei moduli standard, se la risorsa richiesta non è presente tra di essi allora la ricerca prosegue all’interno delle directory definite nel file sys.path. L’elenco di queste directory può essere visualizzato tramite l’importazione del modulo sys che consente di leggere il file stesso:

# Elenco delle directory di sistema di Python
>>> import sys
>>> sys.path
['', 
'C:\\Python\\Lib\\idlelib', 
'C:\\WINDOWS\\SYSTEM32\\python.zip', 
'C:\\Python\\DLLs', 
'C:\\Python\\lib', 
'C:\\Python', 
'C:\\Python\\lib\\site-packages']

Fondamentalmente, nel momento in cui effettua una ricerca di moduli a livello locale, Python esegue una scansione che parte dalla directory locale per poi passare alla PYTHONPATH, cioè la variabile d’ambiente che contiene il percorso alle directory, e, infine, coinvolgere il percorso di default dell’installazione corrente.

La variabile PYTHONPATH viene definita al momento dell’installazione, ma può essere modificata nel caso ve ne sia la necessità:

# Impostazione della PYTHONPATH su Windows
set PYTHONPATH=C:\Python\lib;
# Impostazione della PYTHONPATH sui sistemi Unix based
set PYTHONPATH=/usr/local/lib/python

I namespace

Una volta capito come Python individua i moduli ci si potrebbe chiedere quale sia il meccanismo utilizzato per gestirne i nomi e, soprattutto, le ambiguità in caso di omonimie. Ciò avviene grazie ai namespace che sono dei dizionari contenenti delle chiavi, in pratica nomi di variabili, e valori, oggetti associati alle chiavi. Le istruzioni di Python possono accedere a variabili presenti in namespace locali e globali, nel caso in cui una variabile locale e una globale abbiano il medesimo nome quella locale nasconderà la globale.

Per ciascuna funzione abbiamo uno specifico namespace locale, viene inoltre considerata locale qualsiasi variabile a cui viene assegnato un valore in una funzione. Se si desidera attribuire un valore globale ad una variabile in una funzione sarà necessario fare ricorso alla funzione global.

Quanto esposto può essere esemplificato tramite l’esempio seguente che nel caso del primo codice produrrà un errore a causa della mancata conversione di una variabile in variabile globale, mentre nel secondo funzionerà correttamente in quanto la variabile è stata passata come argomento alla funzione "global":

# Assegnazione di un valore ad una variabile locale
# Viene restituito solo il valore iniziale di x
# La sottrazione produce un errore  
x = 10
 
def xMenoY():

  y = 2
  x = x - y
 
print(x)
xMenoY()
print(x)
# Assegnazione di un valore ad una variabile globale 
# Vengono restituiti il valore iniziale di x e il risultato della sottrazione
x = 10
 
def xMenoY():
  global x
  y = 2
  x = x - y
 
print(x)
xMenoY()
print(x)

Dato che presto riutilizzeremo questo script salviamolo per comodità in un modulo che potremo chiamare, per esempio, "xmenoy.py".

La funzione dir()

Una volta individuato un modulo, Python deve essere in grado reperire le definizioni presenti all’interno di esso. Per capire come funziona questo passaggio è possibile fare riferimento ad una funzione nativa del linguaggio, dir(), che accetta come parametro il nome di un modulo e restituisce in output tutte le definizioni in esso presenti. Perché gli elementi di un modulo risultino accessibili quest’ultimo deve essere innanzitutto importato, se ne potrà poi passare il nome alla funzione:

# Visualizzazione degli elementi di un modulo tramite dir()
>>> import app
>>> dir(app)
['__builtins__', 
'__cached__', 
'__doc__', 
'__file__', 
'__loader__', 
'__name__', 
'__package__', 
'__spec__', 
'mul']

Si noti come l’unica funzione presente in "app", e cioè "mul()", venga elencata per ultima, tutte le altre voci, quelle precedute e seguite dal doppio underscore ("__"), fanno riferimento ad attributi che Python associa al modulo. Tali attributi identificano il modulo attraverso le sue caratteristiche, __name__ contiene per esempio l’informazione relativa al nome del modulo, mentre __file__ quella del percorso del modulo all’interno del sistema:

# Lettura degli attributi di un modulo
>>> import app
>>> app.__name__
'app'
>>> app.__file__
'C:\\Python\\app.py'

Ricaricare un modulo

Una volta che un modulo viene rilevato da Python diventa disponibile in quanto richiamato tramite importazione, è però importante sapere che la sua esecuzione in un’applicazione avviene una sola volta per la sessione corrente.

Per consentire la nuova importazione di un modulo precedentemente importato il linguaggio mette a disposizione la funzione reload() a cui passare come parametro il nome del modulo da ricaricare:

# Ricaricare un modulo
reload(xmenoy)

Il fatto che l’importazione non sia un’operazione persistente consente di migliorare le performance di un script e di limitare il consumo delle risorse a disposizione sul sistema in uso, nello stesso modo un ricorso continuo alle importazioni finirebbe per generare un numero anche rilevante di esecuzioni ripetitive che finirebbero per pesare sulle prestazioni, per questo motivo si è deciso di introdurre la funzione "reload()" da utilizzare soltanto quando si necessita realmente di una nuova importazione.

E’ possibile che dal momento in cui un modulo sia stato importato per la prima volta esso abbia subito delle modifiche, perché queste diventino disponibili Python offre un modulo apposito del quale "reload()" rappresenta un metodo: "imp":

>>> import imp
>>> import xmenoy
10
8
>>> import xmenoy
>>> imp.reload(xmenoy)
10
8
<module 'app' from 'C:\\Python\\xmenoy.py'>

Si noti come nell’esempio proposto la prima importazione di "xmenoy" restituisca il risultato atteso mentre la seconda no in quanto il modulo non è più eseguibile per la sessione; una volta ricaricato, il modulo risulta essere invece nuovamente eseguibile.

Pubblicitร 
Claudio Garau
Claudio Garau
Web developer, programmatore, Database Administrator, Linux Admin, docente e copywriter specializzato in contenuti sulle tecnologie orientate a Web, mobile, Cybersecurity e Digital Marketing per sviluppatori, PA e imprese.
Articolo precedente
Articolo successivo