Python dict / OrderedDict: Assign function to value without executing it immediately

340 Views Asked by At

I have an OrderedDict whose values I would like to be functions, but have encountered unexpected behaviour. Initializing:

from collections import OrderedDict

options_dict=OrderedDict(["A",call_func_A(arg1,arg2)],
                         ["B",call_func_B(arg1,arg3)],
                         ["C",call_func_C(arg1,arg4)]

# Select options
options=["A","C"]

# Execute
result={}
for opt in options:
    result[opt]=options_dict[opt]

# Return result (or whatever)
print result

Functions call_func_A, call_func_B and call_func_C turn out to be executed when options_dict is declared, rather than in the subsequent for loop over options.

I'd like the function calls to wait until the for loop.

What's going on?

2

There are 2 best solutions below

0
stelioslogothetis On

First of all, you are declaring the OrderedDict incorrectly. The constructor expects a list of tuples. Instead, you are giving it multiple lists. Do it like so:

options_dict=OrderedDict([("A",call_func_A(arg1, arg2)),
                          ("B",call_func_B(arg1, arg3)),
                          ("C",call_func_C(arg1, arg4))])

Second, when you declare options_dict, you don't pass the functions as the values of the dict, but rather their results:

options_dict=OrderedDict(["A",call_func_A(arg1,arg2)],
                         ["B",call_func_B(arg1,arg3)],
                         ["C",call_func_C(arg1,arg4)])

You are calling them by doing call_func_A(arg1, arg2). One relatively simple way of avoiding that is by omitting the args:

options_dict=OrderedDict([("A",call_func_A),
                         ("B",call_func_B),
                         ("C",call_func_C)])

You can store the args in a second OrderedDict:

args_dict=OrderedDict([("A",[arg1, arg2]),
                      ("B",[arg3, arg4]),
                      ("C",[arg5, arg6])])

And then to call them:

result={}
for opt in options:
    result[opt]=options_dict[opt](*args_dict[opt])
4
Moses Koledoye On

The functions are called before the dictionary is created. You made the call.

However, you can defer the function calls by nesting it within another function to be called later:

options_dict = OrderedDict([("A", lambda: call_func_A(arg1,arg2)),
                            ("B", lambda: call_func_B(arg1,arg3)),
                            ("C", lambda: call_func_C(arg1,arg4))])

# Select options
options = ["A", "C"]

# Execute
result = {}
for opt in options:
    result[opt] = options_dict[opt]() # <- call

The same effect can be achieved with functools.partial, with an extra import statement to execute.

On another note, since your function arguments are presumably invariant, I don't think when the calls are made is important here. You might as well keep your initial approach of having the functions called at the dict creation time.