Arnold Klein
Arnold Klein

Reputation: 3086

Pass a list of functions with their corresponding parameters to another function

I am struggling to pass a list of functions with a list of corresponding parameters. I also checked here, but it wasn't very helpful. for example (a naive approach which doesn't work):

def foo(data, functions_list, **kwarg):
    for func_i in functions_list:
        print func_i(data, **kwarg)

def func_1(data, par_1):
    return some_function_1(data, par_1)

def func_2(data, par_2_0, par_2_1):
    return some_function_2(data, par_2_0, par_2_1)

foo(data, [func_1, func_2], par_1='some_par', par_2_0=5, par_2_1=11)

Importantly, par_1 cannot be used in func_2, so each function consumes a unique set of parameters.

Upvotes: 3

Views: 1083

Answers (7)

Roee Gavirel
Roee Gavirel

Reputation: 19445

You can do it something like that, "close" each parameters for function in a list item and then let "foo" split it backwards:

def foo(data, functions_list, kwarg):
    for func_i, args in zip(functions_list, kwarg):
        func_i(data, **args)


def func_1(data, par_1):
    print("func_1 %s %s" % (data, par_1))


def func_2(data, par_2_0, par_2_1):
    print("func_2 %s "
          "%s %s" % (data, par_2_0, par_2_1))

data = "Some Data"

foo(data, [func_1, func_2], [{"par_1":'some_par'}, {"par_2_0":5, "par_2_1":11}])

Upvotes: 1

ettanany
ettanany

Reputation: 19806

An approach would be making the 3rd argument of foo a positional argument and pass in a list of args with functions list:

def foo(data, functions_list, args):
    for func, arg in zip(functions_list, args):
        print(func(data, arg))


def func1(data, par_1):
    return 'func1 called with {}'.format(par_1)


def func2(data, par_2):
    return 'func2 called with {}'.format(par_2)

foo('some_data', [func1, func2],
    [
        {'par_1_1': 11, 'par_1_2': 12},
        {'par_2_1': 21, 'par_2_2': 22}
    ])

zip() is used to map each function with the corresponding args.

Output:

func1 called with {'par_1_1': 11, 'par_1_2': 12}
func2 called with {'par_2_1': 21, 'par_2_2': 22}

Upvotes: 1

MSeifert
MSeifert

Reputation: 152597

You could use inspect.getargspec (I assume you use Python 2, you shouldn't use that function in Python 3 because it has been deprecated) to find out which argument names a function has and build a new dictionary based on those:

import inspect

def foo(data, functions_list, **kwargs):
    for func_i in functions_list:
        newkwargs = {name: kwargs[name] 
                     for name in inspect.getargspec(func_i).args 
                     if name in kwargs}
        print(func_i(data, **newkwargs))

def func_1(data, par_1):
    return data, par_1

def func_2(data, par_2_0, par_2_1):
    return data, par_2_0, par_2_1

>>> data = 10
>>> foo(data, [func_1, func_2], par_1='some_par', par_2_0=5, par_2_1=11)
(10, 'some_par')
(10, 5, 11)

But a better way would be to simply associate parameters with functions that doesn't rely on introspection.

Upvotes: 3

VPfB
VPfB

Reputation: 17247

I like @COLDSPEED's approach, but want to present yet another solution. Pass always 3 values: function, args, keyword args:

Usage:

foo(
    func_1, ('some_par',), {},
    func_2, (5, 11), {},
)

Implementation (Python3 syntax):

def foo(*args3):
    while args3:
        func, args, kwargs, *args3 = args3
        func(*args, **kwargs)

Upvotes: 1

arshovon
arshovon

Reputation: 13651

Another approach can be like this:

def foo(data, function_list, **kwargs):
    function_dict = {
        "func_1": func_1,
        "func_2": func_2        
    }
    for func_i in function_list:
        print function_dict[func_i](data, **kwargs)

def func_1(data, **arg):
    filtered_argument = {key: value for key, value in arg.items() if key.startswith('par_1')}
    return list([data, filtered_argument])

def func_2(data, **arg):
    filtered_argument = {key: value for key, value in arg.items() if key.startswith('par_2_')}
    return list([data, filtered_argument])


data = [1,2,3]  
foo(data, ['func_1', 'func_2'], par_1='some_par', par_2_0=5, par_2_1=11)

Output:

[[1, 2, 3], {'par_1': 'some_par'}]
[[1, 2, 3], {'par_2_0': 5, 'par_2_1': 11}]

I am sure that you can improvise your current code as it gets ugly in this way.

Upvotes: 1

Strinnityk
Strinnityk

Reputation: 618

If you want to keep the foo function with that exact same declaration and you don't mind each function receiving the whole set of parameters you could do it like this:

You just need to add to each 'my_*' function the **kwargs parameter.

def foo(data, functions_list, **kwargs):
    for my_function in functions_list:
        print(my_function(data, **kwargs))


def my_sum(a, b, **kwargs):
    return a + b


def my_sub(a, c, **kwargs):
    return a - c


foo(0, [my_sum, my_sub], b=3, c=10)

Python automatically parses kwargs setting the b and c parameters where it has the value.

Upvotes: 2

cs95
cs95

Reputation: 402333

You could use the function's name as the keyword arguments. When indexing kwargs, you'd use func_i.__name__ as the key.

def foo(data, function_list, **kwargs):
    for func_i in function_list:
        print(func_i(data, kwargs[func_i.__name__]))

And now,

foo(data, [func_1, func_2], func_1='some_par', func_2=[5, 11])

Upvotes: 3

Related Questions