Reputation: 2453
I am currently blank on how to do this in an effective way.I thought about using objects but I don't see how they could help in this case. Any ideas?
from random import choice
from copy import deepcopy
def main():
def rand_int():
return choice(['yes', 'no'])
# any nesting, functions possible
spec = {
'answer': rand_int,
'next': {'answer': rand_int},
'the_answer': 42
}
#### looking for elegant (automatic) way to do this
result = deepcopy(spec)
result['answer'] = result['answer']()
result['next']['answer'] = result['next']['answer']()
#### until here
# result2 = ...
print 'result: %s' % result
if __name__ == '__main__':
main()
please do not tell me to use xsd!
Upvotes: 1
Views: 596
Reputation: 89057
You can do this with one line in a dictionary comprehension:
{key: function() for key, function in mydictionary.items()}
Of course, this will throw errors when a value isn't a function, so if that is a possibility, we can simply add a check with the callable()
builtin:
{key: (function() if callable(function) else function) for key, function in mydictionary.items()}
We then need to deal with the fact that your answer needs to be recursive, this makes it a little more complex, but not too hard to fix:
def call_all_callables_in_dict(mapping):
if hasattr(mapping, "items"):
return {key: call_all_callables_in_dict(value) for key, value in mapping.items()}
elif callable(mapping):
return mapping()
else:
return mapping
Note that if you have objects with an items
attribute or method you wish to store in a dict
this function will be run on, this could cause problems. I would recommend changing the name of that attribute or method, or replacing the check with isinstance(dict)
.
I would also like to note that for misleading function names rand_int
that returns a string of 'yes'
or 'no'
is probably about as bad as it gets. Generally you want True
/False
in those situations as well.
As noted in the comments, pre-Python 2.7, you may not have dictionary comprehensions. To get around this, dict()
will take a generator of tuples, so you can replace a dict comprehension like so:
{x: y for x, y in something.items()}
With:
dict((x, y) for x, y in something.items())
So, in full:
from random import choice
def rand_int():
return choice(['yes', 'no'])
spec = {
'answer': rand_int,
'next': {'answer': rand_int},
'the_answer': 42
}
def call_all_callables_in_dict(mapping):
if hasattr(mapping, "items"):
return {key: call_all_callables_in_dict(value) for key, value in mapping.items()}
elif callable(mapping):
return mapping()
else:
return mapping
print(call_all_callables_in_dict(spec))
Gives us:
{'answer': 'no', 'the_answer': 42, 'next': {'answer': 'yes'}}
Upvotes: 8