0
def some_function():
    print("This will always be the same")


def some_other_function(text):
    print(text)


some_variable = "hello"

list_of_elements = [

    "element_one": {
        "name": "static_print",
        "function": some_function
            },

    "element_two": {
        "name": "flexible_print",
        "function": some_other_function
            }
        ]

In the above minimal code example I have several elements in a list, each representing in reality an element (of a game menu I am making with pygame). Each of these elements is a dictionary, with several key:value pairs, one of which is "function". The function then gets selectively called for a chosen element. Sometimes, however, a function stored in this key:value pair needs to be called with arguments, at which point I cannot figure out what to do.

I managed to do this with conditional statements, but this requires checking for each function separately, which circumvents the entire purpose of using this list of dicts as a flexible and generalised way of having multiple (menu) elements, each with its attributes and associated function neatly organised.

Is there an elegant way to do this, and if not what would be your personal approach?

  • 3
    Where will those arguments come from, and at what point? Could you use a [partial function](https://docs.python.org/3/library/functools.html#functools.partial)? – jonrsharpe Jun 01 '21 at 14:41
  • maybe you can always call the function with `*args` and `**kwargs`? Something like `element["function"](*args, **kwargs)` – Matteo Zanoni Jun 01 '21 at 14:55
  • @jonrsharpe the arguments will generally come from local namespace of the function constructing the menu, which is also where the list_of_elements is declared. – CyberGeneticist Jun 01 '21 at 15:06
  • @MatteoZanoni Thank you for your suggestion. Upon thinking about it, I don't see how that could fit into my existing code elegantly, although I think it's likely this would be a good solution for this kind of problem in general, but looking at my code I am not sure it is the right one for me. – CyberGeneticist Jun 01 '21 at 15:09
  • 1
    def run_func(f, a): return f(a) then you can do run_func(, ) on runtime. you can also use map() ( https://www.geeksforgeeks.org/python-pass-multiple-arguments-to-map-function/ ) – Yuri Jun 01 '21 at 15:11
  • 1
    You may perhaps add to every dictionary one more key:value pair, with the list of the variables holding the values that function needs. When you have to call the function you can get the values for the arguments from `locals()`, build the `*args` sequence (or `**kwargs` mapping) and pass it to the function as @Matteo Zanoni suggested – gimix Jun 01 '21 at 15:44
  • @Yuri thank you, although unless I am missing something, this still has the issue of having to check what function is called to decide if and which arguments to pass to it? – CyberGeneticist Jun 01 '21 at 15:50
  • @gimix I think your suggestion is probably the best solution so far I think. Thanks – CyberGeneticist Jun 01 '21 at 15:50

1 Answers1

0

After reading many constructive comments, and some tinkering, I found exactly what I needed.

This solution is largely based on Matteo Zanoni's comment, with some modifications to account for the possibility of missing 'args' and 'kwargs', depending on the particular function being called. This is handled by providing appropriate default values to the 'get' method - an empty list for 'args', and an empty dictionary for 'kwargs'.

For a dictionary structured like so:

dictionary = {
    {
        "function": some_function,
        "args": [1, 4],
    },

    {
        "function": some_other_function,
    },
}

We can do this, which should be flexible to whether or not each 'function' has any associated 'args' or 'kwargs' it needs to be called with:

dictionary[element]["function"](
    *dictionary[element].get("args", []), 
    **dictionary[element].get("kwargs", {}),
)