35

I can put an import statement in a string, exec it, and it works (prints a random digit):

code = """
import random
def f():
    print random.randint(0,9)
"""

def f():
    pass

exec code
f()

Now, if I put exec code and f() in their own function and call it, it doesn't work.

def test():
    exec code
    f()

test()

It says NameError: global name 'random' is not defined.

martineau
  • 119,623
  • 25
  • 170
  • 301
Alex Varga
  • 1,752
  • 2
  • 14
  • 17
  • By curiosity, why would you want to do this? – Vincent Savard Sep 20 '12 at 01:58
  • 1
    This is sign of bad design - avoid. – Burhan Khalid Sep 20 '12 at 10:21
  • 1
    +Vincent Savard, I work with a Java-based product that lets me write Python scripts in it, and I can configure my script to be executed when certain, predefined events happen. However, I cannot use the standard import statement to load another one of my scripts if I wish to use its functions as a library. Instead, I must load the code of that other script from the database and exec() it. And the problem comes in that that other script imports Java libraries that I didn't, in which case calling a library function causes the problem described by +Alex Varga – Preacher Jun 14 '16 at 20:36

3 Answers3

28

How about this:

def test():
    exec (code, globals())
    f()
Yevgen Yampolskiy
  • 7,022
  • 3
  • 26
  • 23
21

What's going on here is that the module random is being imported as a local variable in test. Try this

def test():
    exec code
    print globals()
    print locals()
    f()

will print

{'code': '\nimport random\ndef f():\n    print random.randint(0,9)\n', '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'test': <function test at 0x02958BF0>, '__name__': '__main__', '__doc__': None}
{'random': <module 'random' from 'C:\Python27\lib\random.pyc'>, 'f': <function f at 0x0295A070>}

The reason f can't see random is that f is not a nested function inside of test--if you did this:

def test():
    import random
    def f():
        print random.randint(0,9)
    f()

it would work. However, nested functions require that the outer function contains the definition of the inner function when the outer function is compiled--this is because you need to set up cell variables to hold variables that are shared between the two (outer and inner) functions.

To get random into the global namespace, you would just do something like this

exec code in globals(),globals()

The arguments to exec after the in keyword are the global and local namespaces in which the code is executed (and thus, where name's defined in the exec'd code are stored).

jjm
  • 6,028
  • 2
  • 24
  • 27
8

Specify that you want the global random module

code = """
import random
def f():
  global random
  print random.randint(0,9)
"""

The problem here is that you're importing the random module into your function scope, not the global scope.

Aesthete
  • 18,622
  • 6
  • 36
  • 45