Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I process a python dictionary with callables?

I would like to create a python directory whose values are to be evaluated separately.

So, for example, in the following non-working example I define

a = {'key1': 'value1', 'key2': 42, 'key3': foo(20)}

for which e.g.

def foo(max):
    """Returns random float between 0 and max."""
    return max*random.random()

and after processing of the dict I want to have e.g.

a_processes = {'key1': 'value1', 'key2': 42, 'key3': 12.238746374}

The example does not work since the value of key key3 is immediately evaluated, and foo(20) is no callable. The way it could work would be to use something like

a = {'key1': 'value1', 'key2': 42, 'key3': foo}

but here foo will miss its arguments. One way to handle this would be to define the dict in the following way

a = {'key1': 'value1', 'key2': 42, 'key3': [foo, 20]}

with the following processing scheme

a_processed = dict([k,process(v)] for k,v in a.items())

in which process is a meaningful function checking if its argument is a list, or if the first element is a callable which gets called with the remaining arguments.

My question is about a better way/idea to implement my basic idea?

like image 235
Alex Avatar asked Jan 30 '13 17:01

Alex


1 Answers

Use the functools.partial() to create a callable that'll apply a set of arguments to another callable:

from functools import partial

a = {'key1': 'value1', 'key2': 42, 'key3': partial(foo, 20)}

Now the key3 value is a callable object; a['key3']() will in turn call foo(20) and return the result.

You'd still need to 'process' this dictionary (testing with the callable() function:

{k: v() if callable(v) else v for k, v in a.iteritems()}

of course. If you are using Python 3, use a.items() instead.

A functools.partial setup let's you add additional arguments as needed (provided foo() supports them):

a['key3']('additional', arguments='accepted')

You could even create a dictionary subclass that does this automatically when accessing a key:

def CallablesDict(dict):
    def __getitem__(self, key):
        value = super(CallablesDict, self).__getitem__(key)
        return value() if callable(value) else value

then define a as:

a = CallablesDict({'key1': 'value1', 'key2': 42, 'key3': partial(foo, 20)})

print a['key3']  # calls foo(20)
like image 151
Martijn Pieters Avatar answered Nov 15 '22 20:11

Martijn Pieters