Question

My question is some how related to this question, I have a dictionary of functions with a different number of arguments, how can I call them in an automatic way?

funcdict = {
  'mypackage.mymodule1.myfunction1': mypackage.mymodule.myfunction,
  'mypackage.mymodule2.myfunction2': mypackage.mymodul2.myfunctio2,
    ....
}

funcdict[0](parameter1, parameter2)
funcdict[1](parameter1, parameter2, parameter3)
funcdict[1](parameter1, parameter2, parameter3,parameter4,parameter5)
.........

Instead of calling each of them like above wanna something like this

for i in range(len(funcdict)):
     funcdict[i](......)

Don't know how should I fill the arguments since the number of arguments for each function varies between 2 to 6.

Was it helpful?

Solution

If you want to call all your functions in an automatic way, specifically, all at once, then it isn't important that you be able to call them by some specific name like it was in the question you are referencing. You can just keep the functions in a list and call each one in turn.

As for dealing with a variable number of arguments, Python has a "splat" operator you may have seen in some method declarations: def __init__(self, *args, **kwargs). This operator can be used to unpack argument lists. (See this SO answer for more info)

If you store the parameters in another list you can iterate through your list of functions and apply the parameters to its corresponding function one-by-one using the same syntax and without specifying the number of arguments:

funclist = [lambda x,y: x+y, lambda x,y,z: x+y+z, lambda x: -x]
paramlist = [(1,2), (1,2,3), (1,)]

for index, func in enumerate(funclist):
    print func(*paramlist[index])

How you link the functions to their default parameters is another matter: you could keep them in two lists, or together as tuples in another list or dict, or define a lightweight class to hold them...it depends on your problem. Anyways, it sounds like the important part for you is the * notation.

Edit:

If you want to pass in keyword arguments to the function, you can use the double-* notation to pass a dictionary and have it expanded into the keyword arguments that are required:

def func1(n_samples, noise, factor, random_state):
    print locals()

def func2(n_samples, ratio, noise):
    print locals()

def func3(my_key, default_key='world!'):
    print locals()

funclist = [func1, func2, func3]
paramlist = [
        dict(
            n_samples=1000,
            noise=0.3,
            factor=0.5,
            random_state=1
        ),
        dict(
            n_samples=1000,
            ratio=0.5,
            noise=0.1
        ),
        dict(
            my_key='hello',
        )
]

for index, func in enumerate(funclist):
    func(**paramlist[index])

OTHER TIPS

OK, let's suppose that you put your arguments in a tuple. You will also need to refine your table so you know how many arguments each function takes, which means a modification of your data structure (you could use the inspect module to determine this, but that's an unnecessary complication at present.:

If the functions have to be called an any specific order, by the way, a dict is not a good structure to use because it linearizes in an unpredictable order, but we'll overlook that for now. Here's a chunk of code that uses * notation to pass a tuple as individual arguments. I am using a reporter function to show you have the argument transmission works.

parameters = ("one", "two", "three", "four", "five")

def reporter(*args):
    print("Called with", len(args), "arguments")
    print("args are:", args)

funcdict = {
  'mypackage.mymodule1.myfunction1': (reporter, 1),
  'mypackage.mymodule2.myfunction2': (reporter, 5),
}

for name, (function, arg_ct) in funcdict.items():
    print("Testing", name)
    function(*parameters[:arg_ct])

The output is:

Testing mypackage.mymodule1.myfunction1
Called with 1 arguments
args are: ('one',)
Testing mypackage.mymodule2.myfunction2
Called with 5 arguments
args are: ('one', 'two', 'three', 'four', 'five')

I hope that gives you plenty to chew on.

As a first possibility, one could try to use the code object for the function by getting the func_code property then query co_argcount of this code object.

Here is a small example that calls the functions stored in a dictionary by name, for a set of input arguments, 2, 3, 4 in this case:

#!/usr/bin/env python

def f1(a, b):
    print "executing f1 with %d, %d" % (a, b)
    return a*b

def f2(a, b, c):
    print "executing f2 with %d, %d, %d" % (a, b, c)
    return a*b+c

def call_functions(arg1, arg2, arg3):
    funcdict = {
        "add" : f1,
        "madd" : f2
        }
    for f in funcdict.itervalues():
        if f.func_code.co_argcount == 2:
            f(arg1, arg2)
        elif f.func_code.co_argcount == 3:
            f(arg1, arg2, arg3)
        # and so on

if __name__=="__main__":
    call_functions(2, 3, 4)

Another slightly less verbose possibility (you don't need to explicitely check for the number of arguments for each function, the functions just use what they need) is to use Python's variable argument syntax. This would look as follows:

#!/usr/bin/env python

def f1(*args):
    print "executing f1 with %d, %d" % (args[0], args[1])
    return args[0]*args[1]

def f2(*args):
    print "executing f2 with %d, %d, %d" % (args[0], args[1], args[2])
    return args[0]*args[1]+args[2]

def call_functions(*args):
    funcdict = {
        "add" : f1,
        "madd" : f2
        }
    for f in funcdict.itervalues():
        f(*args)

if __name__=="__main__":
    call_functions(2, 3, 4)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top