LINUX.ORG.RU

Не получается определить функцию внутри 'exec'

 ,


0

1

Вот такой вот пример работает по-разному в python2(2.7.3-r3) и python3(3.2.3-r2).

class A:
    def __init__(self):
        exec('f = lambda x: x')
        print(f(1))

a = A()

В python2 работает и возвращает 1. В python3:

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    a = A()
  File "test.py", line 4, in __init__
    print(f(1))
NameError: global name 'f' is not defined

Хотелось бы понять почему... Никак не могу. Если определять не внутри метода класса - то вроде работает и в python3. А в чём конкретно разница? Ткните носом в ChangeLog - я такого нигде не видел. :( хотя примерно разницу между python2/3 представляю.

P.S. зачем мне это понадобилось - не спрашивайте, перевожу один проектик с 2 на 3, а там вот такие вот костыли, от него так сразу не избавиться.

Это фича. Во первых, если не объявлена переменная, то она ищется в глобальном скоупе, а во вторых:

The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.

Такие дела. Можешь сам подсовывать новую область видимости и брать оттуда результат.

anonymous
()

3.2:

  3           0 LOAD_GLOBAL              0 (exec)
              3 LOAD_CONST               1 ('f = lambda x: x')
              6 CALL_FUNCTION            1
              9 POP_TOP

  4          10 LOAD_GLOBAL              1 (print)
             13 LOAD_GLOBAL              2 (f)
             16 LOAD_CONST               2 (1)
             19 CALL_FUNCTION            1
             22 CALL_FUNCTION            1
             25 POP_TOP
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE

2.7:

  3           0 LOAD_CONST               1 ('f = lambda x: x')
              3 LOAD_CONST               0 (None)
              6 DUP_TOP
              7 EXEC_STMT

  4           8 LOAD_NAME                0 (f)
             11 LOAD_CONST               2 (1)
             14 CALL_FUNCTION            1
             17 PRINT_ITEM
             18 PRINT_NEWLINE
             19 LOAD_CONST               0 (None)
             22 RETURN_VALUE
>>> class A:
...     def __init__(self):
...         exec('f = lambda x: x', globals())
...         print(f(1))
...
>>> a = A()
1

м?

xpahos ★★★★★
()
  1. Не используй exec.
  2. exec во 2-ом и 3-ем питонах это совершенно разные вещи.
  3. Во 2-м питоне exec('f = lambda x: x') правильно записывается как exec 'f = lambda x: x'.

А вообще по существу к комментарию xpahos'а можно добавить только два линка:

ei-grad ★★★★★
()
Последнее исправление: ei-grad (всего исправлений: 2)

Какие-то заумные ответы тут дают. Для простоты: Во 2 питоне exec это оператор, которые используют как локальное то пространство имен, в котором использованы сами. В тройке exec это функция, которая создает свое локальное пространство имен, в котором и определяется «f». Потому последующий код в методе __init__ в 3 питоне ничего про «f» знать не знает.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

В тройке exec это функция, которая создает свое локальное пространство имен, в котором и определяется «f».

Да, ну?

def boo():
    exec('foo = "bar"')
    print(locals())

boo()

Садись, два!

Кстати, хороший вопрос для собеседования, обязательно воспользуюсь.

anonymous
()
Ответ на: комментарий от Virtuos86

А версия, господин преподаватель

Любая, начиная с 3.2. Скорее всего это распространяется и на 3.0/3.1. Если у тебя не работает, то интерпретатор с багом.

anonymous
()
Ответ на: комментарий от anonymous

Очень интересно. А вот так почему?

class A:
    def __init__(self):
        exec('f = lambda x: x')
        g = locals()['f']
        print(g(1))

a = A()

работает

class A:
    def __init__(self):
        exec('f = lambda x: x')
        f = locals()['f']
        print(f(1))

a = A()

валится?

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    a = A()
  File "test.py", line 4, in __init__
    f = locals()['f']
KeyError: 'f'
BattleCoder ★★★★★
() автор топика

Собственно, этот финт ушами var = locals('varname') мне помог, программу свою я починил. Понятно, что exec лучше не пользоваться, позже подумаю, как переписать этот код без него. Всем спасибо.

Тема решена, но вопросы остались ;)

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от anonymous

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

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

валится?

Так организована работа с таблицами символов. Если в коде есть объявление имени, то из exec с ним уже ничего не сделаешь.

anonymous
()
Ответ на: комментарий от BattleCoder

Есть еще момент:

def boo():
    d = locals()
    exec('foo = "bar"', globals(), d)
    foo = d['foo']
    print(foo)

boo()

xDDDD

Все потому, что locals это срез текущего скоупа и обновляется при каждом вызове.

anonymous
()
Ответ на: комментарий от true_admin

вы используете в своём коде exec?

Генерация оберток, чтобы не потерять сигнатуру декорируемой функции. Конфиги. Точка входа для вызова из cli сложных команд.

anonymous
()

Интересно... Тему новую создавать не хочется. Но ещё один интересный пример. Вот думаю, баг или фича?.. может, написать багрепорт pyyaml?

короче, pyyaml - библиотека для питона парсер yaml.

Один и тот же код

import yaml
yaml.dump(lambda x,y,z: x+y+z)

На python2 пишет:

"!!python/name:__main__.%3Clambda%3E ''\n"

python3:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.2/site-packages/yaml/__init__.py", line 200, in dump
    return dump_all([data], stream, Dumper=Dumper, **kwds)
  File "/usr/lib64/python3.2/site-packages/yaml/__init__.py", line 188, in dump_all
    dumper.represent(data)
  File "/usr/lib64/python3.2/site-packages/yaml/representer.py", line 27, in represent
    self.serialize(node)
  File "/usr/lib64/python3.2/site-packages/yaml/serializer.py", line 54, in serialize
    self.serialize_node(node, None, None)
  File "/usr/lib64/python3.2/site-packages/yaml/serializer.py", line 90, in serialize_node
    style=node.style))
  File "/usr/lib64/python3.2/site-packages/yaml/emitter.py", line 115, in emit
    self.state()
  File "/usr/lib64/python3.2/site-packages/yaml/emitter.py", line 228, in expect_document_root
    self.expect_node(root=True)
  File "/usr/lib64/python3.2/site-packages/yaml/emitter.py", line 242, in expect_node
    self.process_tag()
  File "/usr/lib64/python3.2/site-packages/yaml/emitter.py", line 489, in process_tag
    self.prepared_tag = self.prepare_tag(tag)
  File "/usr/lib64/python3.2/site-packages/yaml/emitter.py", line 607, in prepare_tag
    chunks.append('%%%02X' % ord(ch))
TypeError: ord() expected string of length 1, but int found

как думаете, баг?.. всё-таки pyyaml должен поддерживаться веткой python3. практически я вряд ли данную комбинацию буду использовать (не очень читаемый «дамп» получается, потому смысла нет, pickle/shelve никто не отменял для этого если что)

BattleCoder ★★★★★
() автор топика

к слову, yaml.load для того дампа не работает и в python2

BattleCoder ★★★★★
() автор топика
25 ноября 2013 г.
Ответ на: комментарий от anonymous

Добавил в приведенный пример последнюю сточку. Тщетно ожидал, что print найдет значение foo в локальной области видимости функции boo и выведет его. Но не сбылись намеренья благие...

def boo():
    exec('foo = "bar"')
    print(locals())
    print(foo)

boo()

При выполнении получаем:

>>> boo()
{'foo': 'bar'}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in boo
NameError: global name 'foo' is not defined

Понимаю, что чего-то не понимаю. Шаблон трещит, ибо locals() вернула отображение имен в локальной области видимости. Имя foo и значение для него там есть, но print его найти не может. Не ткнет ли кто-нибудь носом, где именно я затупил?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.