Компиляция Python 3.7 с лямбда-выражением не может найти закрывающую функцию - PullRequest
2 голосов
/ 14 июля 2020

Следующее работает и выводит 42, как и ожидалось:

import textwrap
def test_compile():
    code_str = """
    def bump(x):
        return x + 1

    print(); print(bump(41))

    """
    dedented_code_str = textwrap.dedent(code_str)
    code_obj = compile(dedented_code_str, filename='<string>', mode='exec')
    exec(code_obj)

Итак, dump определяется при компиляции print и exe c 'd. Однако вставка лямбда-выражения вызывает исключение из exec, а именно то, что bump не найден (удаление некоторых строк для краткости):

exec(code_obj)
test_compile.py:48: 
<string>:5: in <module>
E   NameError: name 'bump' is not defined

Вот оскорбление попытка скомпилировать:

import textwrap
def test_compile_with_lambda():
    code_str = """
    def bump(x):
        return x + 1

    debug_me = (lambda y: bump(y))(41)
    print(); print(debug_me)

    """
    dedented_code_str = textwrap.dedent(code_str)
    code_obj = compile(dedented_code_str, filename='<string>', mode='exec')
    exec(code_obj)

Код предметной области, а именно

def bump(x):
    return x + 1

debug_me = (lambda y: bump(y))(41)
print(); print(debug_me)

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

Между прочим, если этот предметный код либо вызывается, либо компилируется и (что особенно важно) exe c 'd извне, но в том же файле с test_compile_with_lambda, тогда bump определяется в лямбда-выражении и test_compile_with_lambda работает. Предположительно, в этом случае лямбда-выражение относится к некоему bump, отличному от того, которое находится вместе со строкой его исходного кода.

Я хотел бы иметь возможность скомпилировать то, что мне кажется допустимым лямбда выражения, которые относятся к именам, соответствующим образом определенным в близлежащих контекстах. Можно ли заставить мой test_compile_with_lambda работать без определения bump каким-то неестественным образом?

1 Ответ

4 голосов
/ 14 июля 2020

Проблема в том, что ваше лямбда-выражение создает функцию со свободной переменной bump. Здесь нет никакого закрытия.

Когда вы выполняете код, bump ищется в глобальной области, но bump не определяется в глобальной области: она определяется в локальной области видимости test_compile_with_lambda.

Чтобы продемонстрировать это, передайте текущие locals в качестве глобальной области в exec.

import textwrap
def test_compile_with_lambda():
    code_str = """
    def bump(x):
        return x + 1

    debug_me = (lambda y: bump(y))(41)
    print(); print(debug_me)

    """
    dedented_code_str = textwrap.dedent(code_str)
    code_obj = compile(dedented_code_str, filename='', mode='exec')
    exec(code_obj<b>, locals()</b>)

Вы получите ожидаемый результат 42.

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