ikamen
ikamen

Reputation: 3493

ast execution produces different result in a function vs module scope

Edit: the problem is not related to ast module.

it can be reproduced with just using exec:

y = None

exec("y = 5.0")
print(y)  # prints 5.0

vs

def foo():
    y = None

    exec("y = 5.0")
    print(y)

foo()  # prints None

Original question:

I am generating code programmatically using ast module. I have following code, that works:

import ast

num = ast.Num(n=5.)
y = None
assign = ast.Assign(targets=[ast.Name(id='y', ctx=ast.Store())], value=num)
tree = ast.Module(body=[assign], type_ignores=[])
ast.fix_missing_locations(tree)
c = compile(tree, filename="", mode="exec")
exec(c)  # replaces the value of 'y' with 5.0

print(y) # prints 5.0

However, as soon as I wrap this code in a function, it stops working:

import ast

def foo():
    num = ast.Num(n=5.)

    y = None
    assign = ast.Assign(targets=[ast.Name(id='y', ctx=ast.Store())], value=num)
    tree = ast.Module(body=[assign], type_ignores=[])
    ast.fix_missing_locations(tree)
    c = compile(tree, filename="", mode="exec")
    exec(c)

    print(y)

foo()  # prints None

This is the first time I see python code behave differently after being moved inside a function. I have checked and assignment does not put y in the module (locals) either:

print(y) # NameError: name 'y' is not defined

Upvotes: 1

Views: 214

Answers (2)

Yodogawa Mikio
Yodogawa Mikio

Reputation: 423

When in a function, you can capture the assignments into any dictionary that will function as the scope for the executed statements:

def foo():
    scope = {}
    exec("y = 5.0", scope)
    print(scope['y'])

foo()  # prints 5.0

Upvotes: 1

Rob
Rob

Reputation: 481

Variable Y is declared in function scope and its not available outside of it. If you want Y to be available post function call return it or use a global.

Update: See my comment in relation to exec and scopes: exec() and variable scope

Upvotes: 0

Related Questions