2012-06-01 16 views
10

Sto iniziando a sperimentare con gli strumenti paralleli IPython e ho un problema. Comincio i miei motori di pitone con:Problemi nello spazio dei nomi Python con parallelo ipython

ipcluster start -n 3 

Poi il seguente codice funziona benissimo:

from IPython.parallel import Client 

def dop(x): 
    rc = Client() 
    dview = rc[:] 
    dview.block=True 
    dview.execute('a = 5') 
    dview['b'] = 10 
    ack = dview.apply(lambda x: a+b+x, x) 
    return ack 

ack = dop(27) 
print ack 

ritorna [42, 42, 42] come dovrebbe. Ma se rompo il codice in file diversi: dop.py:

from IPython.parallel import Client 

def dop(x): 
    rc = Client() 
    dview = rc[:] 
    dview.block=True 
    dview.execute('a = 5') 
    dview['b'] = 10 
    print dview['a'] 
    ack = dview.apply(lambda x: a+b+x, x) 
    return ack 

e provare quanto segue:

from dop import dop 
ack = dop(27) 
print ack 

ricevo errori da ogni motore:

[0:apply]: NameError: global name 'a' is not defined 
[1:apply]: NameError: global name 'a' is not defined 
[2:apply]: NameError: global name 'a' is not defined 

I don capisco ... perché non posso inserire la funzione in un file diverso e importarla?

risposta

16

risposta rapida: decorare la vostra funzione con @interactive dal IPython.parallel.util [1] se si desidera avere accesso al namespace globale del motore:

 
from IPython.parallel.util import interactive 
f = interactive(lambda x: a+b+x) 
ack = dview.apply(f, x) 

La spiegazione reale:

lo spazio dei nomi utente IPython è essenzialmente il modulo __main__. È qui che viene eseguito il codice quando si esegue execute('a = 5').

Se si definisce una funzione in modo interattivo, il suo modulo è anche __main__:

 
lam = lambda x: a+b+x 
lam.__module__ 
'__main__' 

quando il motore unserializes una funzione, lo fa nel namespace globale appropriata per il modulo della funzione, in modo che le funzioni definite in __main__ in il tuo cliente è anche definito in __main__ sul motore, e quindi ha accesso a a.

Una volta messo in un file e importarlo, allora le funzioni non sono più attaccati alla __main__, ma il modulo dop:

 
from dop import dop 
dop.__module__ 
'dop' 

Tutte le funzioni convenzionalmente definite in quel modulo (lambda inclusi) avrà questo valore, quindi quando vengono decompressi sul motore, il loro spazio dei nomi globale sarà quello del modulo dop, non__main__, quindi il tuo 'a' non è accessibile.

Per questo motivo, IPython fornisce un semplice decoratore @interactive che restituisce qualsiasi funzione che viene decompressa come se fosse definita in __main__, indipendentemente da dove la funzione è effettivamente definita.

Per un esempio della differenza, prendi questo dop.py:

 
from IPython.parallel import Client 
from IPython.parallel.util import interactive 

a = 1 

def dop(x): 
    rc = Client() 
    dview = rc[:] 
    dview['a'] = 5 
    f = lambda x: a+x 
    return dview.apply_sync(f, x) 

def idop(x): 
    rc = Client() 
    dview = rc[:] 
    dview['a'] = 5 
    f = interactive(lambda x: a+x) 
    return dview.apply_sync(f, x) 

Ora, dop utilizzerà 'a' dal modulo dop, e idop utilizzerà 'un' dal namespace del motore.L'unica differenza tra i due è che la funzione passata da applicare è avvolto in @interactive:

 
from dop import dop, idop 
print dop(5) # 6 
print idop(5) # 10 

[1]: In IPython> = 0,13 (prossimo rilascio), @interactive è disponibile come from IPython.parallel import interactive, anche quando esso sempre dovrebbe sono stato.

Problemi correlati