2014-12-09 14 views
20

Rozważmy następujący przykład:zakres funkcji eval w Pythonie

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']) 

Zwraca:

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

Dlaczego eval nie bierze pod uwagę zmiennych zdefiniowanych wewnątrz funkcji? Z dokumentacji opcjonalnie można przekazać globale i słownik lokalny. Co to znaczy? Wreszcie, jak mogę zmodyfikować ten mały przypadek, aby działał?

+3

można zmodyfikować go do pracy poprzez dodanie 'global' przed deklaracji zmiennej wewnątrz funkcji ale to zły pomysł, z drugiej strony, używanie 'eval' jest zwykle złym pomysłem. – Rusty

+0

Co zardzewiały powiedział - chyba że masz pewność, że masz możliwość używania eval, trzymaj się z dala od niego. – l4mpi

+0

@ l4mpi Wiedziałem, że eval to zły pomysł, ale ja po prostu bawiłem się z lunetami i po prostu nie rozumiałem tego zachowania – Pierpaolo

Odpowiedz

13

Generatory są implemented as function scopes:

Zakres nazw zdefiniowana w bloku klasy jest ograniczony do klasy blok; nie obejmuje ona bloków kodu metod - to zawiera wyrażenia generatora, ponieważ są one implementowane przy użyciu zakresu funkcji o numerze.

Tak więc generator wewnątrz konstruktora dict() ma własny słownik locals(). Teraz rzućmy okiem na Py_eval's source code, specjalnie kiedy zarówno globals() i locals() są None:

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

Tak, dla przykładu PyEval_GetLocals() będzie pusta w momencie, gdy pętla jest wykonującego i globals() będzie globalny słownik. Zauważ, że i, j i k zdefiniowana wewnątrz funkcji nie są w zasięgu lokalnego generatora, a są one w jego zakres załączając:

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

Dzieje się tak dlatego, że wyrażenie generator ma inny zakres do funkcji:

>>> 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}} 

Korzystanie j wewnątrz zakres wiąże go z funkcji, jako że jest najbliższy zakres załączając, ale i i k nie są lokalnie powiązane (ponieważ k nie jest przywoływane, a i służy tylko do utworzenia range).


pamiętać, że można uniknąć tego problemu z:

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

lub słownikiem dosłowny:

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