0

I have this code, which calls a function based on your input key. For example, if you chose pf, it would call the pf function, my_function(20).

My problem is, I know how to call a function without arguments, but I don't know how to do it with arguments. It runs all the functions right now because of the (), but how do I give it argmuents and still call it? Would I have to create a seperate list of arguments?

function_map = {
'pf':['finds the prime factors of a number',my_function(20)]
'cs':['solves a quadratic by completing the square',m.complete_square()]
'sr':['simplifies a radical',m.simplfy_radical(input('> '))]
}

for d in function_map.keys():
  print('{} - {}'.format(d,function_map[d][0])
selection = input('Input keycode >>> ')
if selection in function_map.keys():
  function_map[selection][1]()
lol
  • 481
  • 6
  • 18
  • so you want to get the arguments also from `input`? could you not, instead of putting the function call into your `dict`, just pass the function and call it once you know the arguments? – j-i-l Feb 18 '17 at 00:45
  • Possible duplicate of [Passing functions with arguments to another function in Python?](http://stackoverflow.com/questions/803616/passing-functions-with-arguments-to-another-function-in-python) – TemporalWolf Feb 18 '17 at 00:47
  • `function_map[selection][1]( *args )` is how – TemporalWolf Feb 18 '17 at 00:48

3 Answers3

4

You want functools.partial. partial is so awesome that I literally have the documentation in my bookmarks.

partial is a function that returns a function, but with some of the arguments already set:

Script

from functools import partial


def myfunc(x, y):
    print(x + y)

my_ready_func = partial(myfunc, 3)
my_ready_func(5)
my_ready_func(0)
my_ready_func(10)

Output

8
3
13

If you need to defer the execution of the input function until the actual execution of your function then this will not work as expected.

you might want to write a function that makes your function 'inputtable':

def inputtable(func,query="Whats the argument?"):
    arg = input(query)
    return func(arg)

Then you can store the inputtable function instead of your original one;

'aa':['does stuff', partial(inputtable, function, query=' > ')]

Alternatively you can write a so called decorator to make it inputtable:( I also have decorators in my bookmarks)

def inputtable(func):

    def wrapper():
        arg=input(' > ')
        func(arg)
    return wrapper

And then store it like this:

'aa':['does stuff', inputtable(function)]

Then you don't need to use partial.

Sebastian Wozny
  • 16,943
  • 7
  • 52
  • 69
  • To elaborate on this answer: once you have a `f = partial(my_function, 20)` you can just call it as `f()` with no *additional* args. Which means you can store it in a list as `lst = [ 'foo', f ]` and then call it as `lst[1]()` – aghast Feb 18 '17 at 00:49
  • 1
    and how would you make this work with `input` as the OP requires? – Dimitris Fasarakis Hilliard Feb 18 '17 at 00:50
  • Then add it to your answer, as it is it is implying that `partial` would also work with `input` which isn't the case. – Dimitris Fasarakis Hilliard Feb 18 '17 at 00:53
  • Would storing it like this work? `'aa':['does stuff',partial(function, input(' > '))]` – lol Feb 18 '17 at 00:53
  • if I made a function like this: `def ask(string): return input(string)` would that work as an argument? `'aa':['does stuff', partial(myfunc, ask('your name > '))]` – lol Feb 18 '17 at 01:10
  • @Sebatian Wozny, I will accept your answer if you clarify that one part. – lol Feb 18 '17 at 01:20
  • @lol no unfortunately it would not. The compiler will encounter the `ask` reference and try to execute it because it's followed by `()`. Then `input()` will be called. – Sebastian Wozny Feb 18 '17 at 01:22
  • How would I go about doing it with 2 arguments and one of them input? – lol Feb 18 '17 at 01:30
  • Then you need partial and the second version of inputtable: `inputtable(partial(func,5))`Will you accept my answer? – Sebastian Wozny Feb 18 '17 at 01:34
2

An alternative is to use lambdas that won't evaluate (merely compile) their bodies until called:

function_map = {
    'pf':['finds the prime factors of a number',lambda: my_function(20)]
    'cs':['solves a quadratic by completing the square',lambda: m.complete_square()]
    'sr':['simplifies a radical',lambda: m.simplfy_radical(input('> '))]
}
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
0

function_map[selection][1]( *args ) is how, but you first have to remove the (20), ()... from the dict, as that calls the function and puts it's result in the dict, rather than storing the function itself: I then added an additional entry to the list which specifies the number of arguments.

function_map = {
'pf':['finds the prime factors of a number',my_function, 1]
'cs':['solves a quadratic by completing the square',m.complete_square, 0]
'sr':['simplifies a radical',m.simplfy_radical, 1]
}

for d in function_map.keys():
  print('{} - {}'.format(d,function_map[d][0])
selection = input('Input keycode >>> ')
if selection in function_map.keys():
  args = []
  for _ in function_map[selection][2]:
    args.append(input('Input arg>>> '))
  if args:
    function_map[selection][1]( *args )
  else:
    function_map[selection][1]()
TemporalWolf
  • 7,727
  • 1
  • 30
  • 50
  • @lol so you need to determine how many args it takes. I've updated it... you may also have to change the type... but this shows you how. – TemporalWolf Feb 18 '17 at 00:54