2014-12-09 11 views
20

Si consideri il seguente esempio:portata della funzione eval in python

i=7 
j=8 
k=10 
def test(): 
    i=1 
    j=2 
    k=3 
    return dict((name,eval(name)) for name in ['i','j','k']) 

Restituisce:

>>> test() 
{'i': 7, 'k': 10, 'j': 8} 

Perché eval non prende in considerazione le variabili definite all'interno della funzione? Dalla documentazione, facoltativamente puoi passare un globals e un dizionario dei locali. Cosa significa? Infine, come posso modificare questo piccolo caso per farlo funzionare?

+3

è possibile modificare a lavorare aggiungendo 'global' prima dichiarazione di variabile all'interno di una funzione ma questa è una cattiva idea, d'altra parte, l'uso di "eval" è di solito una cattiva idea. – Rusty

+0

Ciò che ha detto arrugginito - a meno che tu non sia sicuro che tu debba usare eval, stare lontano da esso. – l4mpi

+0

@ l4mpi Sapevo che l'eval è una cattiva idea, ma stavo solo giocando con gli ambiti, e non capivo questo comportamento – Pierpaolo

risposta

13

generatori sono implemented as function scopes:

L'ambito di nomi definiti in un blocco di classe è limitato alla classeBlocco; non si estende ai blocchi di codice dei metodi: questo include le espressioni del generatore poiché sono implementati utilizzando uno scope di funzione .

Quindi, il generatore all'interno del costruttore dict() ha il proprio dizionario locals(). Ora diamo uno sguardo a Py_eval's source code, specialmente quando sia globals() e locals() sono None:

if (globals == Py_None) { 
     globals = PyEval_GetGlobals(); 
     if (locals == Py_None) 
      locals = PyEval_GetLocals(); 
    } 

Quindi, per il tuo esempio PyEval_GetLocals() sarà vuoto al momento del ciclo è in esecuzione e globals() sarà il dizionario globale. Si noti che i, j e k definito all'interno della funzione non sono in ambito locale del generatore, anzi sono nel suo ambito di inclusione:

>>> dict((name,eval(name, globals(), {})) for name in ['i', 'j', 'k']) 
{'i': 7, 'k': 10, 'j': 8} 
4

Ciò si verifica perché l'espressione generatore ha un diversa portata al funzione:

>>> def test(): 
    i, j, k = range(1, 4) 
    return dict((j, locals()) for _ in range(i)) 

>>> test() 
{2: {'.0': <listiterator object at 0x02F50A10>, 'j': 2, '_': 0}} 

Uso j all'interno della portata lega dalla funzione, in quanto questo è l'ambito racchiude più vicino, ma i e k non sono associati localmente (come k non viene fatto riferimento e i viene utilizzato solo per creare il range).


noti che è possibile evitare questo problema con:

return dict(i=i, j=j, k=k) 

o un dizionario letterale:

return {'i': i, 'j': j, 'k': k} 
Problemi correlati