Python 3.2.1: exec ('x = y ()') устанавливает значение в игрушечном примере, но не в полном коде - PullRequest
2 голосов
/ 02 января 2012

Я использую выражение exec () для установки значения, например, так:

foo = 3
def return_4():
    return 4
instruction = 'foo = return_4()'
exec(instruction)                     # <---- WHERE THE MAGIC HAPPENS
print(foo)

Это получается как 4, как я и ожидал.

В моей программе есть операции для манипулирования кубиком Рубика.В этой урезанной версии я сделаю четыре вещи:

  1. Я создам экземпляр куба, заполнив одну грань (с аббревиатурами «передний верхний левый» и «передний нижний правый»' и тому подобное).

  2. У меня будет функция, которая вращает эту переднюю грань.

  3. У меня будет функция «интерпретатор», которая берет куб и список инструкций и применяет эти инструкции к кубу, возвращая модифицированный куб.Вот где я использую 'exec' (и где я думаю, что происходит поломка).

  4. Наконец, я запусту интерпретатор на моем частичном кубе с инструкцией, чтобы повернуть лицо один раз.

+

my_cube = [['FTL', 'FTM', 'FTR',
            'FML', 'FMM', 'FMR',
            'FBL', 'FBM', 'FBR'],
            [],[],[],[],[]] # other faces specified in actual code

def rotate_front(cube):
    front = cube[0]
    new_front = [front[6],front[3],front[0],
                 front[7],front[4],front[1],
                 front[8],front[5],front[2]]
    # ...
    ret_cube = cube
    ret_cube[0] = new_front
    # pdb says we are returning a correctly rotated cube,
    # and calling this directly returns the rotated cube
    return ret_cube

def process_algorithm(cube=default_cube, algorithm=[]):
    return_cube = cube
    for instruction in algorithm:
        exec('return_cube = ' + instruction + '(return_cube)')  # <--- NO MAGIC!
        # ACCORDING TO pdb, return_cube HAS NOT BEEN ROTATED!
    return return_cube

process_algorithm(cube = my_cube, algorithm = ['rotate_front'])

Если заменить замену exec (x = y) на x = eval (y), похоже, это сработает.return_cube = eval (инструкция + '(return_cube)')

Так что, возможно, это просто академический.Почему пример с игрушкой работает, а реальный код не работает?(Я делаю что-то очевидное и глупое, например, пропустить знак равенства? Я собираюсь надрать себя, держу пари ...)

Спасибо за любую помощь, которую кто-либо может предложить.

Ответы [ 2 ]

5 голосов
/ 02 января 2012

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

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

exec(some_code, globals())

Чтобы это работало, вам нужно добавить global my_var внутри функции, чтобы убедиться, что поиск будет работать.

Имейте в виду, что эти вещи будут вставлены в глобальное пространство имен вашего модуля ...

Кстати, зачем вам exec или eval?Почему вы не можете добавить реальные функции в свой список algorithm?


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

В целях безопасности измените его на None и создайте новый список, если необходимо.

3 голосов
/ 02 января 2012

Это не попытка ответить на вопрос , а скорее расширяет контрольные примеры, так что поведение можно увидеть лучше и сохранить для справки.Результаты получены из Python 3.2.2 для Windows.См. Ответ Дж. Бернардо, чтобы узнать «почему» такое поведение.

Из глобальной области («пример игрушки»):

>>> foo = "global-foo"
>>> exec('foo = "global-bar"')
>>> foo
'global-bar'

В функции (полный контекст):

>>> def setIt():
        foo = "local-foo"
        exec('foo = "local-bar"')
        return foo

foo = "global-foo"
>>> setIt()
'local-foo'
>>> foo
'global-foo'

С указанием globals() (вообще не работает для locals()):

>>> def setIt():
        foo = "local-foo"
        exec('foo = "local-bar"', globals())
        return foo

>>> foo = "global-foo"
>>> setIt()
'local-foo'
>>> foo
'local-bar'

Счастливое кодирование.

...