简体   繁体   中英

Why variables inside a function don't get searched in provided global namespace when using timeit or exec function?

As a minimal example, I want to timeit fn function which has a x variable inside. So I passed {'x': 20, 'fn': fn} as my global namespace:

from timeit import timeit

def fn():
    print('This line executed.')
    return x

print(timeit('fn()', globals={'x': 20, 'fn': fn}, number=1_000_000))

I thought that because x is not local, Python checks the global namespace to find it. Then I get back "This line executed." followed by a NameError which says: name 'x' is not defined . But Python sees {'x': 20, 'fn': fn} namespace because when I remove the 'fn': fn part, the error changes to name 'fn' is not defined .

Same is true for exec function(If that helps or has a relationship):

def fn():
    print('This line executed.')
    return x

s = """def fn():
    print('This line executed.')
    return x"""

exec('fn()', {'x': 10, 'fn': fn})

How can I introduce x to these functions? Is it The only option to define them as module-level variables and pass globals() dictionary to global argument?

Each module has it's own global variables, when we say 'fn': fn we mean:

  • use fn in it's own module (eg answer.py ) which has it's own global variables.

So if there is no global variable like x in answer.py , fn cannot call it and you will get NameError .

Here is an example:

# content of answer.py

x = 10


def k1():
    print(f'{globals()["x"]}')


s = """
print(globals()['x'])
"""

# uses current's module ('answer.py') global variables to run script in `s`
exec(s)

# creates new global variables to run script in `s`
exec(s, {'x': 20})

# uses current's module global variables to call `k1` in module 'answer.py' which has his own global variables
exec('k1()')

# creates new global variables to call `k1` in module 'answer.py' which has his own global variables
# when we say 'k1': k1, it means use `k1` in module 'answer.py' which has his own global variables
exec('k1()', {'k1': k1, 'x': 20})

results:

10
20
10
10

so if you want to tell those functions to use your x in your global variables you have some options such as:

  • change the global variables of that module
  • don't use a module function and write it in a string and pass your global variables to it

As document says about globals() :

Return a dictionary representing the current global symbol table. This is always the dictionary of the current module ( inside a function or method, this is the module where it is "defined", not the module from which it is called ).

This specifically addressed my issue. So when when Python tries to find x inside fn function, (because it's not local or non-local) it will go and find where it is defind and look at global namespace which the function is defined in , not the global dictionary we provide as a global namespace.

Although {'x': 20, 'fn': fn} is the global dictionary which Python can find the label fn , but the fn function itself is not defined there, it is only called there.

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