简体   繁体   中英

Why exec() works differently when invoked inside of function and how to avoid it

I'm trying to declare two functions within exec statement in python. Let's call them f1() and f2() .

I've found out that when exec is invoked inside of some function, then f2() has no visibility of f1() . However this doesn't happen when exec and function call are placed in global code.

# Case 1: Working fine

code = """
def f1(): print "bar"
def f2(): f1()
"""

exec(code)
f2() # Prints "bar" as expected
# Case 2: Throws NameError: global name 'f1' is not defined

code = """
def f1(): print "bar"
def f2(): f1()
"""

def foo():
    exec(code)
    f2() # NameError

foo()

Can someone explain me how to avoid that NameError and make exec work inside of a function?

exec() accepts a second parameter for globals . As explained in the docs :

Note The built-in functions globals() and locals() return the current global and local dictionary, respectively, which may be useful to pass around for use as the second and third argument to exec().

So you can make this work by explicitly passing in globals() :

code = """
def f1(): print ("bar")
def f2(): f1()
"""

def foo():
    exec(code, globals())
    f2() # works in python2.7 and python3

foo()

If you want to control the scope precisely, you can pass an object into exec :

code = """
def f1(): print ("bar")
def f2(): f1()
"""

def foo():
    context = {}
    exec(code, context)
    context['f2']() 

foo() 

Kevin's comment is a good one and bears some repeating - using exec is dangerous there is almost always a better approach.

However, in response to your question in the first case, both f1() and f2() are in the global namespace so when you call f2() it can find f1(). In the second case, they are created in the local space of the foo() function. When f2() is called it can't find the local f1() definition.

You can fix this using:

code = """
global f1
def f1(): print "bar"
def f2(): f1()
"""
def foo():
    exec(code)
    f2()
foo()

Again - this is almost certainly not the way you want to solve this problem.

** EDIT ** Posted wrong version of the code which I had been checking, this version is what I had meant to include.

"In all cases, if the optional parts [of exec()] are omitted, the code is executed in the current scope."

https://docs.python.org/3.5/library/functions.html#exec

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM