Elia Giaccardi Old
Elia Giaccardi Old

Reputation: 262

Python - How to call every function in a list with a single function call

I'm trying to create a dictionary where each value is a list of functions that should get executed when calling the value. Here's some pseudo-code:

functions = {
    "A": (execute() for execute in [func1(param1), func2(param1)]),
    "B": (execute() for execute in [func1(param2), func2(param2)]),
}

But if I try to call this using functions["A"]() an exception is thrown saying "TypeError: 'generator' object is not callable".

How should I approach this problem?

EDIT: Just to clarify, the functions are not generators nor have any return type. They just execute some actions based on the parameter.

Upvotes: 0

Views: 809

Answers (5)

flyakite
flyakite

Reputation: 799

Just leave out the brackets:

param1 = 5
param2 = 10

def func1(a):
    return a + a

def func2(a):
    return a * a

functions = {
    'A': [func1(param1), func2(param1)],
    'B': [func1(param2), func2(param2)]
}

print(functions['A']) 
###output: [10, 25]

Upvotes: 0

Elia Giaccardi Old
Elia Giaccardi Old

Reputation: 262

ACCEPTED ANSWER:

Thanks to all of your suggestions, I came up with a solution. I'm just going to leave the code here:

def move(params):  # params is a list of 5 parameters of different types
    # Execute move

def rotate(params):  # params is a list of 2 parameters of different types
    # Execute rotation


moves: {
    "A": [(move, [param1, param2 etc...]), (rotate, [other_param1, other_param2])
    "B": [(move, [param3, param4 etc...]), (rotate, [other_param3, other_param4])
}


def execute_move(move):
    for func, params in moves[move]:
        func(params)


# Example use
execute_move("A")
execute_move("B")
execute_move("A")

This works really well, so thank you all for your help.

Upvotes: -1

krs
krs

Reputation: 4154

So, we have a few issues with your pseudo code!

[func1(param1), func2(param1)] will actually call func1 and func2 and store their return values in the list which is not what you want. Then these return values has to be callable, else the execute() part wont make any sense.

So you need a list of functions to call, and a list of parameters to send into these functions.

So if we instead have a list of tuples to work with we can do something like

to_call = [(len, "hello"), (len, "world!")]

for func, param in to_call:
    func(param)

Will print 5 and 6, so all good.

Then to do this in one function call and return their return values, we can just wrap it in a function

from typing import Sequence, Tuple, Callable, Any # Typing is cool now kids.

def call_list(functions: Sequence[Tuple[Callable, Any]]) -> list[Any]: 
    results = list()
    for func, param in functions:
        results.append(func(param))
    return results

now we can call that function

>>> call_list([(len, "hello"), (len, "world!")])
-> [5, 6]

And with a bit of list comprehension

def call_list(functions: Sequence[Tuple[Callable, Any]]) -> list[Any]: 
    return [func(param) for func, param in functions]

now we can do

functions = {
    "A": [(func1, param1), (func2, param1)],
    "B": [(func1, param2), (func2, param2)],
}

call_list(functions["A"])

Then its up to your api design how you want it to actually look like, for example if the param is external and you want to apply a series of functions of it, skip the tuple and just have a list of functions

def call_list(functions: Sequence[Callable], *args: Any) -> list[Any]: 
    return [func(*args) for func in functions]

functions = {
    "A": [func1, func2],
    "B": [(func3, func4],
}

call_list(functions["A"], param1)
call_list(functions["A"], param1, param2, param3)

Upvotes: 2

Adon Bilivit
Adon Bilivit

Reputation: 27114

As has been pointed out, the use of a generator is flawed.

Also, in the original code, both func1 and func2 would have to return references to other functions in order for that to work

It's more likely that what you need is something like this:

def func1(p):
    pass

def func2(p):
    pass

functions = {
    "A": [func1, func2]
}

param1 = 0

for func in functions['A']:
    func(param1)

Upvotes: 0

mousetail
mousetail

Reputation: 8010

I don't think you want to create a generator at all.

You can try something like this:

# A function that returns a different function that calls each function in functions in sequence
def call_functions(functions, param):
    # Define a inner function
    def _inner():
        # For each function
        for function in functions:
            # Call it
            function(param)
    # return the inner function
    return _inner

# Define a dict mapping strings to functions.
to_call = {
    "A": call_functions([func1, func2], param1),
    "B": call_functions([func1, func2], param2)
}

to_call["A"]()
# This will do basically the same as:
# > func1(param1)
# > func2(param1)

Upvotes: 0

Related Questions