Почему exec не работает в функции с подфункцией? - PullRequest
53 голосов
/ 19 декабря 2010

Похоже, вы не можете использовать exec в функции, которая имеет подфункцию ...

Кто-нибудь знает, почему этот код Python не работает? Я получаю ошибку в exec в test2. Кроме того, я знаю, что exec не очень хороший стиль, но поверьте мне, я использую exec по соответствующей причине. Я не использовал бы это иначе.

#!/usr/bin/env python
#

def test1():
    exec('print "hi from test1"')

test1()

def test2():
    """Test with a subfunction."""
    exec('print "hi from test2"')
    def subfunction():
        return True

test2()

РЕДАКТИРОВАТЬ: я сузил ошибку, чтобы иметь функцию в подфункции. Это не имеет ничего общего с ключевым словом повышения.

Ответы [ 6 ]

65 голосов
/ 19 декабря 2010

Правильно.Вы не можете использовать exec в функции, которая имеет подфункцию, если вы не укажете контекст.Из документов:

Если в функции используется exec и функция содержит вложенный блок со свободными переменными, компилятор вызовет SyntaxError, если только exec явно не задает локальное пространство имен для exec.(Другими словами, «exec obj» будет недопустимым, но «exec obj in ns» будет допустимым).

Для этого есть веская причина, которую я, вероятно, понял бы, если бы онаВоскресный вечер.Теперь следующий вопрос: почему вы используете exec?Это очень редко нужно.Вы говорите, у вас есть веская причина.Я скептически отношусь к этому.;) Если у вас есть веская причина, я расскажу вам обходной путь.: -P

Ну да ладно, вот и все:

def test2():
    """Test with a subfunction."""
    exec 'print "hi from test2"' in globals(), locals()
    def subfunction():
        return True
28 голосов
/ 20 декабря 2010

Хотя в Python выглядит, как будто локальные переменные хранятся в словаре locals(), обычно это не так.Вместо этого они в основном хранятся в стеке и доступны по индексу.Это делает поиск локальной переменной быстрее, чем если бы он каждый раз делал поиск по словарю.Если вы используете функцию locals(), то вы получите свежий словарь, созданный из всех локальных переменных, и поэтому присвоение locals() обычно не работает.

Есть пара исключений дляэтот сценарий:

Когда вы используете неквалифицированную exec внутри функции, Python отключает оптимизацию и использует реальный словарь для локальных переменных.Это означает, что вы можете создавать или обновлять переменные внутри exec, но это также означает, что доступ к локальным переменным в этой функции будет выполняться медленнее.

Другое исключение состоит в том, что при вложении функций внутренняя функцияполучить доступ к локальным переменным в области видимости внешней функции.Когда это происходит, переменная сохраняется в объекте 'cell', а не в стеке.Дополнительный уровень косвенности делает все использование переменных области действия медленнее при доступе к ним из внутренней или внешней функции.

Вы столкнулись с тем, что эти два исключения из того, как локальные переменные обычно хранятся, несовместимы,Вы не можете хранить переменную в словаре и одновременно обращаться к ней через ссылку на ячейку.Python 2.x исправляет это, запрещая exec, даже в таких случаях, когда вы не пытаетесь использовать переменные с определенными областями действия.

6 голосов
/ 28 декабря 2016

Это довольно интересный случай:

>>> def func():
...     exec('print "hi from func"')
...     def subfunction():
...         return True
... 
  File "<stdin>", line 2
SyntaxError: unqualified exec is not allowed in function 'func' because 
it contains a nested function with free variables

Причина, по которой это действительно не работает, заключается в том, что subfunction содержит свободную переменную, и, поскольку в Python 2, exec теоретически может изменять локальные переменные в содержащей области, было бы невозможно решить, если переменная должен быть связан с глобальной или родительской областью функции. Один из стихов в Дзэн Питона: «В свете двусмысленности откажитесь от соблазна догадаться». и это то, что делает Python 2.

Теперь вопрос: что это за свободная (несвязанная) переменная? Ну, это True!

Действительно, это можно воспроизвести с None:

>>> def func():
...     exec('print "hi from func"')
...     def subfunction():
...         return None
... 
  File "<stdin>", line 2
SyntaxError: unqualified exec is not allowed in function 'test2' because it contains a nested
function with free variables

Несмотря на то, что None нельзя присвоить, и это рассматривается как константа в байт-коде, ошибочный анализатор считает, что это несвязанная переменная.

Но если вы замените его на 1, и он будет работать без проблем:

>>> def test2():
...     exec('print "hi from func"')
...     def subfunction():
...         return 1
... 
>>>

Чтобы избежать этой ошибки, укажите явно глобальные и, возможно, локальные, которые будут использоваться exec, скажем:

>>> def test2():
...     exec 'print "hi from test2"' in {}
...     def subfunction():
...         return None
...
>>>

В Python 3 exec - это простая функция, которая не обрабатывается специально синтаксическим анализатором или компилятором байт-кода. В Python 3 exec не может перепривязать имена локальных функций, и, таким образом, этой ошибки синтаксиса и неоднозначности не существует.


Один специфический случай совместимости с Python 2 и 3 заключается в том, что в документации по Python 2.7 говорится, что

Форма exec(expr, globals) эквивалентна exec expr in globals, а форма exec(expr, globals, locals) эквивалентна exec expr in globals, locals. Форма кортежа exec обеспечивает совместимость с Python 3, где exec является функцией, а не оператором.

Форма кортежа не всегда была на 100% совместимой, так как ошибка при обработке exec в функциях с вложенными функциями (выпуск 21591) ; до Python 2.7.8 следующий код мог вызвать исключение:

def func():
    exec('print "hi from test2"', {})
    def subfunction():
        return None

Это исправлено в Python 2.7.9 и больше не выдает.

4 голосов
/ 19 декабря 2010

Это хорошо работает в Python 3.1.3, после изменения оператора печати для использования функции печати. ​​

В Python 2.6 он выдает SyntaxError: unqualified exec is not allowed in function 'test2' it contains a nested function with free variables, я не думаю, что это ошибка.

1 голос
Понимания

dict и list также могут рассматриваться как подфункции на Python 2.7.5

Например, это не работает на Python 2.7.5, но работает на Python 2.7.12:

def func():
    exec('print("a")')
    (str(e) for e in range(10))

с:

  File "./a.py", line 4
    exec('print("a")')
SyntaxError: unqualified exec is not allowed in function 'func' it contains a nested function with free variables

Вероятно, он был внутренне скомпилирован с функцией в байт-коде.

TODO находит фиксацию фиксации.Это было за пределами моего git log --grep foo.

Аналогично для dict понимания:

def func():
    exec('print("a")', {e:str(e) for e in range(10)})

, что особенно плохо, так как это общий параметр для аргумента global.

Также поднят в: https://github.com/sphinx-doc/sphinx/issues/5417#issuecomment-421731085

1 голос
/ 19 декабря 2010

Ошибка кажется мне достаточно очевидной:

SyntaxError: unqualified exec is not allowed in function 'test2' it contains a nested function with free variables

См. Pep 227 для получения дополнительной информации: http://www.python.org/dev/peps/pep-0227/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...