1

I wrote a function def_function, that dynamically defines another function at runtime.

main.py

#!/usr/bin/python3

def def_function(name):
  lines = list()
  lines.append('global {}\n'.format(name))
  lines.append('def {}():\n'.format(name))
  lines.append('  print(\'{}() has been called!\')\n'.format(name))
  code = ''.join(lines)
  function = compile(code, '', 'exec')
  exec(function)

def_function('foo')
foo()

Running main.py gives the expected output:

foo() has been called!

Now I'd like to move the definition of def_function to module.py and import it from main.py.

module.py

def def_function(name):
  lines = list()
  lines.append('global {}\n'.format(name))
  lines.append('def {}():\n'.format(name))
  lines.append('  print(\'{}() has been called!\')\n'.format(name))
  code = ''.join(lines)
  function = compile(code, '', 'exec')
  exec(function)

main.py

#!/usr/bin/python3

from module import def_function

def_function('foo')
foo()

This results in the following error:

Traceback (most recent call last):
  File "./main.py", line 6, in <module>
    foo()
NameError: name 'foo' is not defined

I've already googled my problem and read various questions at SO, but I couldn't find a solution. Please help me.

3
  • Did you know that you can write nested function definitions in Python? Commented May 22, 2021 at 21:16
  • I know, but as far as I understand the code of a nested function can't be generated at runtime. My real function generates code depending on further arguments to def_function. Commented May 22, 2021 at 21:40
  • all functions in Python are created at runtime! (Except the built-in ones which are not written with def ...) Commented May 22, 2021 at 21:43

2 Answers 2

1

You define foo in module.py scope. Do not forget import module.py.

Call foo(), as module.foo().

import module

module.def_function('foo')
module.foo()

May help you https://realpython.com/python-scope-legb-rule/

Additional

Add this after exec(function). Also import sys.

setattr(sys.modules['__main__'], name, globals()[name])

Setattr it's function assigns the value to the attribute of object or create new attribute. https://docs.python.org/3/library/functions.html#setattr

module.py

import sys


def def_function(name):
    lines = list()
    lines.append('global {}\n'.format(name))
    lines.append('def {}():\n'.format(name))
    lines.append('  print(\'{}() has been called!\')\n'.format(name))
    code = ''.join(lines)
    function = compile(code, '', 'exec')
    exec(function)
    setattr(sys.modules['__main__'], name, globals()[name])

main.py

from module import def_function

def_function('foo')
foo()
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the possible workaround/solution. Is there a way to define foo in the global scope from within module.py?
@DnsLnk Yes, you can.
Honestly, there are other ways to solve this, but more context is needed.
Thank you for the additional info! Thats exactly what I was looking for. I will upvote your answer, as soon as I have sufficient reputation. :)
0

Your example can be written like this, with a nested function definition and no compile or exec:

def def_function(name):
    def f():
        print(name, "has been called")
    return f

# ...

foo = def_function("foo")

You have to return it, however.

1 Comment

Thanks but if I knew the code of foo before runtime, then I would just define it like any other function. My real def_function contains various ifs, elses and loops and generates foo depending on further arguments. I don't think that can be done with a nested function definition. I might be wrong though.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.