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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.