2015-08-12 3 views
2

В следующем коде первая ссылка на iszero прошла успешно, а вторая (внутри лямбда) дает NameError: 'iszero' is not defined.Ошибка Python lambda + exec

myblock = """ 
def iszero(x): 
    return x == 0 

print(iszero(0)) #works 

args = [0, 1, 2] 

flts = list(filter(lambda f:(not iszero(f)), args)) #NameError 
""" 

mylocals = {} 
exec(myblock, globals(), mylocals) 
+3

Могу ли я спросить, почему ваш скрипт использует не только 'lambda', но также' exec() 'и' globals() '? – TigerhawkT3

+0

@ user5090812 Вы подаете в суд на 'Python3'? –

+0

@ gsb-eng 'Python 3.4.3' – user5090812

ответ

2

Я бы назвал это ошибкой Python (по крайней мере, ошибкой документации).

Вы должны открыть вопрос в Python bug tracker...

Проблема не связана с lambda, выглядит как замыкания, созданных Exec, когда предусмотрен locals словарь не имеют правильного разрешения области видимости.

Любопытно, что PyPy имеет такое же поведение, поэтому может быть Это действительно ожидаемый результат (однако я не могу понять, где он документирован).

EDIT

Это может показаться странным, но это ожидаемое поведение.

Причина заключается в том, что, когда exec обеспечивается как в global и local словаря код выполняется, как если бы он был в теле определения класса.

Этот контекст довольно специфичен, и созданные в нем замыкания не могут получить доступ к именам, определенным в области действия класса (поэтому вам нужно использовать myclass.myattribute в методах доступа к атрибутам класса - и это поиск, начинающийся с глобального).

Это странное поведение часто не кусается, потому что обычно код в области определения класса просто выполняет простые назначения (для атрибутов уровня класса) и определения (для методов), которые не пытаются захватить эти имена.

0

Здесь я понял, что это exec(myblock), это всегда добавляет объекты к locals(). Я проверил приведенные ниже сценарии.

Так как python управляет locals и globals, когда вы используете exec.

if (globals == Py_None) { 
    globals = PyEval_GetGlobals(); 
    if (locals == Py_None) { 
     locals = PyEval_GetLocals(); 
     if (locals == NULL) 
      return NULL; 
    } 
    if (!globals || !locals) { 
     PyErr_SetString(PyExc_SystemError, 
         "globals and locals cannot be NULL"); 
     return NULL; 
    } 
} 
else if (locals == Py_None) 
    locals = globals; 

В соответствии с приведенной выше логикой, когда вы делаете exec(myblock, None, None) питона получает globals() и работает равным это выражение exec(myblock, globals(), globals()), как задорно вышеприведенная логика. ниже - простое выражение для проверки, является ли locals() is globals() или нет, когда мы предоставляем как None.

>>> exec("print(locals() is globals())", None, None) 
True 

Таким же образом, когда мы обеспечиваем locals, как None, в соответствии с приведенной выше питона логики if (locals == Py_None) locals = globals, как будет ссылаться на тот же объект, и это globals.

>>> exec("print(locals() is globals())", globals(), None) 
True 

И когда мы проходим locals как пустая Словаре {}, согласно вышеуказанной логике он не будет делать каких-либо изменений в локальных переменных.

>>> exec("print(locals() is globals())", globals(), {}) 
False 

Теперь давайте посмотрим, Что происходит в вашем случае ..

Я изменил сценарий немного и добавил print(list(globals()), list(locals())); print(locals() is globals()) эти две строки, как показано ниже.

myblock = """ 
def iszero(x): 
    return x == 0 

args = [0, 1, 2] 

# added these two stmts here 
print(list(locals())) 
print(list(globals())) 
print(locals() is globals()) 

flts = list(filter(lambda f:(not iszero(f)), args)) #NameError 
""" 

И после выполнения указанных выше строк с помощью exec с locals, как пустым Dict.

>>> exec(myblock, globals(), {}) 
['iszero', 'args'] 
['__package__', 'myblock', '__builtins__', '__spec__', '__doc__', '__loader__', '__name__'] 
False 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<string>", line 12, in <module> 
    File "<string>", line 12, in <lambda> 
NameError: name 'iszero' is not defined 

Очевидно, что locals пространство это получение добавлены все атрибуты. поэтому вы никогда не увидели бы iszero метод в globals, если вы пройдете locals как {}.

И причина вы получаете NameError для закрытия list(filter(lambda f:(not iszero(f)), args)) он начал поиск в globals вместо locals и ваших Глобал не метод определен.

+0

Если код 'exec' помещен внутри простой функции, то' globals() 'и' locals() 'изнутри функции выглядят так же, как и здесь; в частности, 'iszero' находится внутри локалей функции, а не' globals'; но ссылка на 'iszero' внутри лямбда-работ. Почему лямбда-функция не выглядит в локаторах охватывающей области в моем примере - разве это не точка закрытия? – user5090812