1

In our code, comprehensions are sometimes called from an exec inside a function's body. The behavior however, is not similar to other calls to comprehension:

foo = 1
bar = [i for i in (0,) if foo]
print(bar)

-> [0]

LIST_COMPREHENSION = \
"""
foo = 1
bar = [i for i in (0,) if foo]
print(bar)
"""

exec(LIST_COMPREHENSION)

-> [0]

def f():
    foo = 1
    bar = [i for i in (0,) if foo]
    print(bar)
f()

-> [0]

def g():
    exec(LIST_COMPREHENSION)
g()

->

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-0cc9a6403ba4> in <module>
      1 def g():
      2     exec(LIST_COMPREHENSION)
----> 3 g()

<ipython-input-2-0cc9a6403ba4> in g()
      1 def g():
----> 2     exec(LIST_COMPREHENSION)
      3 g()

<string> in <module>

<string> in <listcomp>(.0)

NameError: name 'foo' is not defined
AturSams
  • 7,568
  • 18
  • 64
  • 98
  • 1
    `exec` is commencing a fresh process. Processes don't share scope/memory/resources/etc. If you start a new process you need to make sure it has `bar` defined. – El. Jul 17 '22 at 10:40
  • 1
    @El. But you can see this line: `exec("""print('bar' in locals())""")` and it suggests bar is defined. That is the question. – AturSams Jul 17 '22 at 10:53
  • 1
    As already mentioned, `exec` execute the code in a fresh process. so the `locals` in the code section point to the freshly commenced process, not the altered(parent) process. – El. Jul 17 '22 at 11:00
  • @El. Wait, when you run this: `def foo(): bar = 'x' exec("""print('bar' in locals())""") foo()` You get True (not False) – AturSams Jul 17 '22 at 11:17
  • No, It's `true`. It's thoroughly weird behavior. :)) my guess is that the process gets the locals in the code section as a regular dictionary but not the one that is on the process scope like the `bar` at the last line. – El. Jul 17 '22 at 11:22
  • Check this one: `def g(): exec(""" foo = 1 bar = [i for i in (0,) if foo] print(bar) """) g()` Will put it in the quesiton – AturSams Jul 17 '22 at 11:32
  • 4
    Contrary to what the other user is claiming, `exec` does not launch a new process. You can pause execution during `exec` and examine the `ps` output if you want to confirm. – user2357112 Jul 17 '22 at 11:53
  • 1
    `def g(): exec(LIST_COMPREHENSION, {})` works (or even with `globals()`-dict) but be sure to run it in a clean and fresh context to avoid possible conflicts with other variables – cards Jul 17 '22 at 19:00
  • @cards why does: `def g(): exec(LIST_COMPREHENSION, {}, {}) g()` fail? – AturSams Jul 18 '22 at 06:57
  • @AturSams yes, it fails with double `{}`... it seems to be a scoping problem: if you try as mentioned in my previous comment you will see that the `foo` and `bar` variables doesn't belong to the scope of `g` (`locals()` is empty even after `exec`!) but to the globals only. So with `exec(..., {}, {})` the scopes are set to empty – cards Jul 18 '22 at 15:18

0 Answers0