2013-02-16 19 views
7

Ho definito tre funzioni che dovrebbero modificare una variabile globale x.Differenza tra "globale" e "import __main__"

def changeXto1(): 
    global x 
    x = 1 

def changeXto2(): 
    from __main__ import x 
    x = 2 

def changeXto3(): 
    import __main__ 
    __main__.x = 3 

x = 0 
print x 
changeXto1() 
print x 
changeXto2() 
print x 
changeXto3() 
print x 

Si dà il risultato:

0 
1 
1 
3 

changeXto1 utilizza l'istruzione globale normale. Il risultato è come previsto x == 1. changeXto2 utilizza from __main__ import per indirizzo x. Questo non funziona. Successivamente, x è ancora 1. changeXto3 utilizza import main per indirizzo x tramite __main__.x. Il risultato dopo è 3 come previsto.

Perché from __main__ import non funziona in changeXto2, mentre import __main__ funziona in changeXto3? Perché abbiamo bisogno di una dichiarazione globale in Python se possiamo indirizzare le variabili globali anche con il modulo __main__?

+0

changeXto2 imposta solo una variabile locale: http://www.saltycrane.com/blog/2008/01/python-variable- note sullo schema/ –

+0

Non vedo nulla sull'importazione dei moduli qui. –

risposta

10

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 
+0

Grazie per la risposta molto completa. Ho sempre pensato che 'import module' e' from module import' fossero gli stessi eccetto che le variabili sono chiamate diversamente. Vedo che c'è una differenza, quando la nuova variabile viene creata usando 'from import'. La variabile nel modulo originale mantiene lo stesso, ma la variabile nello spazio dei nomi locale è stata appena creata e associata al nome locale. – Holger

8

Perché non from __main__ import lavoro in changeXto2, mentre import __main__ sta lavorando in changeXto3?

Funziona bene, semplicemente non fa quello che vuoi. Copia il nome e il valore nello spazio dei nomi locale invece di avere il codice per l'accesso allo spazio dei nomi di __main__.

Perché abbiamo bisogno di una dichiarazione globale in Python se possiamo indirizzare le variabili globali anche con il modulo __main__?

Perché solo fanno la stessa cosa quando il codice viene eseguito in __main__. Se stai eseguendo, per esempio, othermodule dopo averlo importato, lo __main__ si riferirà allo script principale e nonothermodule.

Problemi correlati