Questo è correlato al modo in cui Python traduce il codice in bytecode (il passo della compilazione).
Durante la compilazione di una funzione, Python tratta tutte le variabili assegnate come variabili locali ed esegue un'ottimizzazione per ridurre il numero di ricerche di nomi che dovrebbe fare. Ad ogni variabile locale viene assegnato un indice e, quando viene chiamata la funzione, il loro valore verrà memorizzato in uno stack array locale indirizzato per indice. Il compilatore emetterà LOAD_FAST
e STORE_FAST
opcode per accedere alla variabile.
La sintassi global
indica al compilatore che anche se alla variabile è assegnato un valore, non deve essere considerata una variabile locale, non deve essere assegnato un indice. Utilizzerà invece LOAD_GLOBAL
e STORE_GLOBAL
opcode per accedere alla variabile. Questi opcode sono più lenti poiché usano il nome per eseguire una ricerca in molti dizionari (locali, globali).
Se si accede a una variabile solo per leggere il valore, il compilatore emette sempre LOAD_GLOBAL
poiché non sa se si suppone che sia una variabile locale o globale e quindi assume che sia globale.
Quindi, nella prima funzione, utilizzando global x
si informa il compilatore che si desidera che tratti l'accesso in scrittura a x
come scrittura su una variabile globale anziché su una variabile locale. I codici operativi per la funzione di mettere in chiaro:
>>> dis.dis(changeXto1)
3 0 LOAD_CONST 1 (1)
3 STORE_GLOBAL 0 (x)
6 LOAD_CONST 0 (None)
9 RETURN_VALUE
Nel suo terzo esempio, si importa il modulo __main__
in una variabile locale chiamata __main__
e quindi assegnare al suo x
campo. Poiché i moduli sono oggetti che memorizzano tutti i mapping di livello superiore come campi, si assegna alla variabile x
nel modulo __main__
. E come hai trovato, i campi del modulo __main__
si collegano direttamente ai valori nel dizionario globals()
perché il tuo codice è definito nel modulo __main__
. I codici operativi mostrano che non si accede direttamente x
:
>>> dis.dis(changeXto3)
2 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 0 (None)
6 IMPORT_NAME 0 (__main__)
9 STORE_FAST 0 (__main__)
3 12 LOAD_CONST 2 (3)
15 LOAD_FAST 0 (__main__)
18 STORE_ATTR 1 (x)
21 LOAD_CONST 0 (None)
24 RETURN_VALUE
Il secondo esempio è interessante. Poiché si assegna un valore alla variabile x
, il compilatore assume che si tratti di una variabile locale e che esegue l'ottimizzazione. Quindi, lo from __main__ import x
importa il modulo __main__
e crea un nuovo bind il valore di x
nel modulo __main__
nella variabile locale denominata x
. Questo è sempre il caso, from ${module} import ${name}
basta creare un nuovo bind il namespace corrente. Quando si assegna un nuovo valore alla variabile x
, si modifica semplicemente il bind corrente, non il bind nel modulo __main__
non correlato (sebbene se il valore è mutabile e lo si muta, la modifica sarà visibile attraverso tutti i bind). Ecco i codici operativi:
>>> dis.dis(f2)
2 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 2 (('x',))
6 IMPORT_NAME 0 (__main__)
9 IMPORT_FROM 1 (x)
12 STORE_FAST 0 (x)
15 POP_TOP
3 16 LOAD_CONST 3 (2)
19 STORE_FAST 0 (x)
22 LOAD_CONST 0 (None)
25 RETURN_VALUE
Un buon modo per pensare a questo è che in Python tutti assegnazione sono vincolanti un nome a un valore in un dizionario, e dereferenziare è solo facendo una ricerca dizionario (questa è un'approssimazione , ma abbastanza vicino al modello concettuale). Quando si esegue obj.field
, si sta cercando il dizionario nascosto di obj
(accessibile tramite obj.__dict__
) per la chiave "field"
.
Quando si ha un nome di variabile nuda, viene cercato nel dizionario , quindi nel dizionario globals()
se è diverso (sono gli stessi quando il codice viene eseguito a livello di modulo). Per un compito, inserisce sempre il binding nel dizionario locals()
, a meno che non dichiari di volere un accesso globale eseguendo global ${name}
(questa sintassi funziona anche a livello superiore).
Quindi tradurre la vostra funzione, questo è quasi se avesse scritto:
# NOTE: this is valid Python code, but is less optimal than
# the original code. It is here only for demonstration.
def changeXto1():
globals()['x'] = 1
def changeXto2():
locals()['x'] = __import__('__main__').__dict__['x']
locals()['x'] = 2
def changeXto3():
locals()['__main__'] = __import__('__main__')
locals()['__main__'].__dict__['x'] = 3
changeXto2 imposta solo una variabile locale: http://www.saltycrane.com/blog/2008/01/python-variable- note sullo schema/ –
Non vedo nulla sull'importazione dei moduli qui. –