0

Basically what I am looking for is the equivalent of itertools.product but for dicts.

For example, say I want to test a function for all combinations of its keyword arguments, I would like to pass lists of permissible values for each keyword argument and then get back a list of dicts.

kwargs_options = {"debug": ["on", "off"], 
                  "locale":["de_DE", "en_US", "fr_FR"]

expected ouput:

[{'debug': 'on', 'locale': 'de_DE'},
 {'debug': 'on', 'locale': 'en_US'},
 {'debug': 'on', 'locale': 'fr_FR'},
 {'debug': 'off', 'locale': 'de_DE'},
 {'debug': 'off', 'locale': 'en_US'},
 {'debug': 'off', 'locale': 'fr_FR'}]

In order to have a similar API to itertools.product this should return an iterator, which can then be wrapped with a call to list(...) if desired.

Example call:

list(dictproduct(kwargs_options}))

This is similar to Combinations from dictionary with list values using Python but returns a list of two-element dicts rather than two single-element dicts.

Community
  • 1
  • 1
snth
  • 5,194
  • 4
  • 39
  • 48
  • 2
    Self-answering is fine, but *the question* still needs to be on-topic. This question, without the answer for context, is unanswerable. – Martijn Pieters Jan 26 '17 at 09:57
  • 1
    Including an actual concrete example, with sample inputs and expected output would go a long way towards making this question clearer. – Martijn Pieters Jan 26 '17 at 09:58
  • Apologies, I came up with the answer when looking at another question (which I referenced) and wanted to share it. So I rushed in the question. I have now made it more clear. – snth Jan 26 '17 at 13:01

1 Answers1

4

This is what I have come up with:

from itertools import product

def dictproduct(dct):
    for t in product(*dct.itervalues()):
        yield dict(zip(dct.iterkeys(), t))

which gives us

>>> list(dictproduct({"debug":["on", "off"], "locale":["de_DE", "en_US", "fr_FR"]}))
[{'debug': 'on', 'locale': 'de_DE'},
 {'debug': 'on', 'locale': 'en_US'},
 {'debug': 'on', 'locale': 'fr_FR'},
 {'debug': 'off', 'locale': 'de_DE'},
 {'debug': 'off', 'locale': 'en_US'},
 {'debug': 'off', 'locale': 'fr_FR'}]

This can also be used to efficiently call a function with all combinations of keyword arguments:

def kwproduct(**kwargs):
    return dictproduct(kwargs)

def f(x, y):
    return 10*x + y

for kwargs in kwproduct(x=[1,2], y=[3,4,5]):
    print "f({x},{y})={z}".format(z=f(**kwargs), **kwargs)

output:

f(1,3)=13
f(2,3)=23
f(1,4)=14
f(2,4)=24
f(1,5)=15
f(2,5)=25

The benefit of kwproduct over dictproduct is that you don't need to create a dict but it obviously limits you to use valid argument names as keys.

snth
  • 5,194
  • 4
  • 39
  • 48