78

How to dynamically create a function in Python?

I saw a few answers here but I couldn't find one which would describe the most general case.

Consider:

def a(x):
    return x + 1

How to create such function on-the-fly? Do I have to compile('...', 'name', 'exec') it? But what then? Creating a dummy function and replacing its code object for then one from the compile step?

Or should I use types.FunctionType? How?

I would like to customize everything: number of argument, their content, code in function body, the result, ...

Ecir Hana
  • 10,864
  • 13
  • 67
  • 117
  • 5
    possible duplicate of [True dynamic and anonymous functions possible in Python?](http://stackoverflow.com/questions/10303248/true-dynamic-and-anonymous-functions-possible-in-python) – Martijn Pieters Jul 02 '12 at 10:02
  • I don't think it a duplicate: `dynf = FunctionType(compile('def f(x): return x + 3', 'dyn.py', 'exec'), globals())` and `print dynf(1)` breaks with `TypeError: '() takes no arguments (1 given)'` – Ecir Hana Jul 02 '12 at 10:06
  • 2
    Just because the answer there might be wrong doesn't mean this isn't a duplicate question. – Martijn Pieters Jul 02 '12 at 10:10
  • The linked question has an updated answer demonstrating how to create a function with arguments. – Martijn Pieters Jul 02 '12 at 14:32
  • are you sure you can't achieve what you want using functional programming as it exists in python (lambdas, closures, compositions...)? the code objects are fiddly, and they're not well documented (or really at all). Plus they're considered internals, subject to change with or without notice. – Xingzhou Liu Jul 20 '17 at 22:21

9 Answers9

43

Use exec:

>>> exec("""def a(x):
...   return x+1""")
>>> a(2)
3
mavroprovato
  • 8,023
  • 5
  • 37
  • 52
37

Did you see this, its an example which tells you how to use types.FunctionType

Example:

import types

def create_function(name, args):
    def y(): pass

    y_code = types.CodeType(args,
                            y.func_code.co_nlocals,
                            y.func_code.co_stacksize,
                            y.func_code.co_flags,
                            y.func_code.co_code,
                            y.func_code.co_consts,
                            y.func_code.co_names,
                            y.func_code.co_varnames,
                            y.func_code.co_filename,
                            name,
                            y.func_code.co_firstlineno,
                            y.func_code.co_lnotab)

    return types.FunctionType(y_code, y.func_globals, name)

myfunc = create_function('myfunc', 3)

print repr(myfunc)
print myfunc.func_name
print myfunc.func_code.co_argcount

myfunc(1,2,3,4)
# TypeError: myfunc() takes exactly 3 arguments (4 given)
Azat Ibrakov
  • 9,998
  • 9
  • 38
  • 50
Ahsan
  • 11,516
  • 12
  • 52
  • 79
29

If you need to dynamically create a function from a certain template try this piece:

def create_a_function(*args, **kwargs):

    def function_template(*args, **kwargs):
        pass

    return function_template

my_new_function = create_a_function()

Within function create_a_function() you can control, which template to chose. The inner function function_template serves as template. The return value of the creator function is a function. After assignment you use my_new_function as a regular function.

Typically, this pattern is used for function decorators, but might by handy here, too.

kdw
  • 399
  • 3
  • 3
  • Thank you! Exactly what I was looking for. My use case was to create a function based on one of my dynamic generated class attribute. But it should not depend on 'self'. Therefore it is a static func `create_a_function` with the parameter as an argument. Thereby the attribute can be used in the inner function as a normal variable and the function is detached :-) – Nils Aug 15 '20 at 09:53
  • 2
    it has some limit, can't be serialized by pickle – Fu Hanxi Mar 31 '21 at 08:07
14

You can use lambda for this.

a = lambda x: x + 1
>>> a(2)
3
Asclepius
  • 57,944
  • 17
  • 167
  • 143
vishnu m c
  • 841
  • 1
  • 7
  • 21
  • 2
    This is almost the one I'm using now. In my case I need to dynamically build a function as a string or to precompile a configured string expression, so I used `a = eval("lambda x: x + 1")` instead, but the result is the same. In my case I also provide math functions to expressions, so I also added `from math import *` in the python scripts that evaluate and execute the lambda. – Chris Tophski Dec 02 '19 at 17:52
  • Note that you can use this to create a list or dict of functions that can be run like `generic['d'](3)` – endolith Dec 19 '20 at 03:27
9

What about this approach?

In this example I'm parametrizing first order functions on one variable (x -> ax+b) in one class:

class Fun: 
  def __init__(self, a,b):
    self.a, self.b = a,b

  def f(self, x):
    return (x*self.a + self.b)

 u = Fun(2,3).f

Here u will be the function x->2x+3.

Berci
  • 544
  • 1
  • 7
  • 10
  • there's always `functools.partial` applied to lambdas as well: `u = partial(lambda a, b, x: x*a + b, 2, 3)` – cowbert May 12 '23 at 16:03
4

You can do at this manner:

new_func='def next_element(x):\n  return x+1'
the_code=compile(new_func,'test','exec')
exec(the_code)
next_element(1)

It's similar to the previous exec solution.

Helio Junior
  • 153
  • 2
  • 15
1

simpler than Berci's answer

def get_fn(a, b): # factory function
  def fn(): # result function
    print(a, b)
  return fn

fn = get_fn(1, 2)

fn()

this is useful to turn variables into constants ("template variables for dynamic functions")

milahu
  • 2,447
  • 1
  • 18
  • 25
0

In my case I need a function returning the function to save it into a variable:

def create_function(function_name, function_arguments, function_code):
    function_string = f"def {function_name}({function_arguments}): {function_code}"
    exec(function_string)
    return_value = eval(function_name)
    return return_value

name = 'say_hello'
arguments = 'name'
code = 'print(f"Hello, {name}!")'

myfunc = create_function(name, arguments, code)

print(myfunc, type(myfunc))
myfunc('John Doe')
Rubén Pozo
  • 1,035
  • 1
  • 12
  • 23
0

for the sake of completeness, the way how dynamically created function works can be simply parametrized

def dynamically_create_greeting(greet_style):
    def inner(name):
        print(f'{greet_style} {name}')

    return inner


say_hello = dynamically_create_greeting(greet_style='hello')
say_hello('John')

say_hi = dynamically_create_greeting(greet_style='hi')
say_hi('John')

this produces output >

hello John

hi John

stam
  • 175
  • 9