2014-12-09 5 views
20

Рассмотрим следующий пример:объем Eval функции в 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']) 

Она возвращает:

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

Почему Eval не принимает во внимание переменные, определенные внутри функции? Из документации вы можете передать глобальные и локальные словари. Что это значит? Наконец, как я могу изменить этот маленький случай, чтобы заставить его работать?

+3

вы можете изменить его на работу, добавив 'global' Перед объявлением переменной внутри функции но это плохая идея, с другой стороны, использование 'eval', как правило, является плохой идеей. – Rusty

+0

Что сказал ржавый - если вы не уверены, что хотите использовать eval, держитесь подальше от него. – l4mpi

+0

@ l4mpi Я знал, что eval - плохая идея, но я просто играл с областями, и я просто не понял этого поведения – Pierpaolo

ответ

13

Генераторы implemented as function scopes:

Область имен определяется в блоке класса ограничивается классом блок; он не распространяется на кодовые блоки методов - этот включает в себя выражения генераторов, поскольку они реализованы с использованием области действия .

Таким образом, генератор внутри конструктора dict() имеет свой собственный словарь locals(). Теперь давайте взглянем на Py_eval's source code, особенно когда оба globals() и locals() не None:

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

Так, для примера PyEval_GetLocals() будет пустым в данный момент цикл исполнения и globals() будет глобальный словарь. Обратите внимание, что i, j и k определены внутри функции, не в локальной области генератора, а они в своей области видимости:

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

Это происходит потому, что выражение генератора имеет различного объем к функции:

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

Использование j внутри рамка связывает его из функции, так как это ближайшее охватывающая область применения, но i и k не связаны локально (так как k не ссылается и i используется только для создания range).


Обратите внимание, что вы можете избежать этой проблемы с:

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

или словарь буквальным:

return {'i': i, 'j': j, 'k': k} 
Смежные вопросы