Apo
Apo

Reputation: 338

How can I dynamically define a function from user input?

I'm trying to dynamically generate a function from user input. The user gives a function as a string input, gives the differents parameters, and I want to write a function that will change this string into a usable function. My idea so far was to use the exec function to create a lambda function constructed with a string like so : exec("f = lambda {}:{}".format(', '.join(['x'] + parameter_list), function_string)). This would give, for example, a string like "f = lambda x, a:x+a" to the exec function.

This technique works fine if I define the scope of exec to globals(), but I'd like my function to be local so that I can do something like this:

def define_function(function_string, parameter_list):
    exec("f = lambda {}:{}".format(', '.join(['x'] + parameter_list), function_string))
    return f

or like this :

def define_function(function_string, parameter_list):
    exec("return lambda {}:{}".format(', '.join(['x'] + parameter_list), function_string))

However, in the first case, it gives an error along the lines of "f is not defined". In the second case, meanwhile, it says that "return can only be used in a function". I understand why the second error occurs, but I wonder why my first try didn't work. Using globals() makes it work, but I'd rather not use it.

If you have any suggestions for other methods for achieving the same result, I'm interested in those as well.

EDIT

Using eval also raises an error :

line 9, in define_function
    eval("f = lambda {}:{}".format(', '.join(['x'] + parameter_list), function_string))
  File "<string>", line 1
    f = lambda x, a:x+a
      ^
SyntaxError: invalid syntax

Upvotes: 1

Views: 149

Answers (1)

Alex Waygood
Alex Waygood

Reputation: 7559

Solution 1: use eval():

def get_func(param_list, result_str):
    return eval("lambda {}: {}".format(', '.join(['x'] + param_list), result_str))

Solution 2: if you really want to use exec(), do something like this:

def get_func(param_list, result_str):
    exec("f = lambda {}: {}".format(', '.join(['x'] + param_list), result_str))
    return locals()['f']

Some good info on the differences between exec and eval here: What's the difference between eval, exec, and compile?


I can see what you're trying to do in your question, by the way, and it's worth noting that the documentation pages for exec() and locals() both explicitly warn that this won't work. Ditto, if you type help(locals) into the interactive interpreter, you get this message:

Help on built-in function locals in module builtins:

locals()
    Return a dictionary containing the current scope's local variables.

NOTE: Whether or not updates to this dictionary will affect name lookups in
the local scope and vice-versa is *implementation dependent* and not
covered by any backwards compatibility guarantees.

Upvotes: 1

Related Questions