2010-08-21 1 views
51

Supponiamo che io sono un pacchetto chiamato bar, e contiene bar.py:Come modificare una variabile del modulo da un altro modulo?

a = None 

def foobar(): 
    print a 

e __init__.py:

from bar import a, foobar 

Poi ho eseguire questo script:

import bar 

print bar.a 
bar.a = 1 
print bar.a 
bar.foobar() 

Ecco quello che mi aspetto:

None 
1 
1 

Ecco quello che ottengo:

None 
1 
None 

Qualcuno può spiegare la mia idea sbagliata?

risposta

47

si sta utilizzando from bar import a. a diventa un simbolo nell'ambito globale del modulo di importazione (o in qualunque ambito si verifichi l'istruzione import). Pertanto, quando assegni un nuovo valore a a, devi solo modificare il valore a e non il valore effettivo. prova a importare bar.py direttamente con import bar in __init__.py e conduci qui l'esperimento impostando bar.a = 1. In questo modo, stai effettivamente modificando bar.__dict__['a'], che è il valore "reale" di a in questo contesto.

È un po contorto con tre strati, ma bar.a = 1 cambia il valore della a nel modulo chiamato bar che è effettivamente derivato da __init__.py. Non cambia il valore di a che foobar vede perché foobar risiede nel file effettivo bar.py. È possibile impostare bar.bar.a se si desidera cambiarlo.

Questo è uno dei pericoli di utilizzare la from foo import bar forma dell'istruzione import: si divide bar in due simboli, uno visibile globalmente dall'interno foo che inizia indicando il valore originale e un altro simbolo visibile nell'ambito dove l'istruzione import viene eseguita. Cambiare un punto in cui un simbolo non cambia il valore che ha indicato.

Questo tipo di cose è un killer quando si prova a reload un modulo dall'interprete interattivo.

14

Una fonte di difficoltà con questa domanda è che si dispone di un programma di nome bar/bar.py: import bar importazioni sia bar/__init__.py o bar/bar.py, a seconda di dove lo si fa, che lo rende un po 'ingombrante per tenere traccia che a è bar.a.

Ecco come funziona:

La chiave per capire ciò che accade è rendersi conto che nella tua __init__.py,

from bar import a 

in effetti fa qualcosa di simile

a = bar.a # … with bar = bar/bar.py (as if bar were imported locally from __init__.py) 

e definisce una nuova variabile (bar/__init__.py:a, se lo si desidera). Pertanto, il numero from bar import a in __init__.py associa il nome bar/__init__.py:a all'oggetto originale bar.py:a (None). Questo è il motivo per cui si può fare from bar import a as a2 in __init__.py: in questo caso, è chiaro che avete sia bar/bar.py:a e distinta nome della variabile bar/__init__.py:a2 (nel tuo caso, i nomi delle due variabili appena capita di essere entrambi a, ma vivono ancora in diversi spazi dei nomi: in __init__.py, sono bar.a e a).

Ora, quando si fa

import bar 

print bar.a 

si accede a variabili bar/__init__.py:a (dal import bar Le importazioni della vostra bar/__init__.py). Questa è la variabile che modifichi (in 1). Non stai toccando il contenuto della variabile bar/bar.py:a. Così, quando in seguito si fa

bar.foobar() 

si chiama bar/bar.py:foobar(), che accede variabile a da bar/bar.py, che è ancora None (quando foobar() è definito, si lega i nomi delle variabili una volta per tutte, in modo che il a in bar.py è bar.py:a, nessuna altra variabile a definita in un altro modulo, poiché potrebbero esserci molte variabili a in tutti i moduli importati). Da qui l'ultima uscita None.

5

Per mettere altro modo: Questo malinteso risulta molto facile da realizzare. It is sneakily defined in the Python language reference: l'uso di oggetto anziché simbolo. Vorrei suggerire che il riferimento al linguaggio Python rendono questo più chiaro e meno radi ..

La forma from non vincola il nome del modulo: si passa attraverso l'elenco degli identificatori , sembra ognuno di loro nella modulo trovato nel passaggio (1) e associa il nome nello spazio nomi locale all'oggetto quindi trovato .

TUTTAVIA:

Quando si importano, si importa il valore corrente del simbolo importato e aggiungerlo al tuo spazio dei nomi come definito.Non si sta importando un riferimento, si sta effettivamente importando un valore.

Pertanto, per ottenere il valore aggiornato di i, è necessario importare una variabile che contiene un riferimento a tale simbolo.

In altre parole, l'importazione NON è come una import nella dichiarazione JAVA, external in C/C++ o anche una clausola use in PERL.

Piuttosto, la seguente dichiarazione in Python:

from some_other_module import a as x 

è più come il seguente codice nel K & RC:

extern int a; /* import from the EXTERN file */ 

int x = a; 

(avvertimento: nel caso Python, "a" e "x" sono essenzialmente un riferimento al valore reale: non stai copiando l'INT, stai copiando l'indirizzo di riferimento)

+0

In realtà, trovo la P il modo di importare di ython è molto più pulito di quello di Java, perché gli spazi dei nomi/gli ambiti devono sempre essere separati correttamente e non interferire mai l'uno con l'altro in modi imprevisti. Come qui: la modifica di un'associazione di un oggetto a un nome in uno spazio dei nomi (leggi: assegnazione di qualcosa alla proprietà globale di un modulo) non influisce mai su altri spazi dei nomi (leggi: il riferimento che è stato importato) in Python. Ma lo fa in Java, ecc. In Python hai solo bisogno di capire cosa viene importato, mentre in Java, devi anche capire l'altro modulo nel caso, altera questo valore importato in seguito. – Tino

+0

Devo essere in disaccordo abbastanza strenuamente. L'importazione/inclusione/utilizzo ha una lunga storia di utilizzo del modulo di riferimento e non della forma del valore in nust su ogni altra lingua. –

+0

Dannazione, colpisco il ritorno ... c'è un'aspettativa di importazione per riferimento (come per @OP). In effetti, un nuovo programmatore Python, a prescindere da quanto esperto, deve essere raccontato in questo modo "guarda fuori". Questo non dovrebbe mai accadere: basato su un uso comune, Python ha preso la strada sbagliata. Creare un "valore di importazione" se necessario, ma non confondere simboli con valori al momento dell'importazione. –