Подтвердить что ты не робот

Комбинации словаря со значениями в списке с использованием Python

У меня есть следующее входящее значение:

variants = {
  "debug" : ["on", "off"],
  "locale" : ["de_DE", "en_US", "fr_FR"],
  ...
}

Я хочу обработать их, чтобы получить следующий результат:

combinations = [
  [{"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"}]
]

Это должно работать с произвольной длиной ключей в словаре. Играл с itertools в Python, но не нашел ничего соответствующего этим требованиям.

4b9b3361

Ответ 1

import itertools as it

varNames = sorted(variants)
combinations = [dict(zip(varNames, prod)) for prod in it.product(*(variants[varName] for varName in varNames))]

Hm, это возвращает:

[{'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'}]

который, вероятно, не совсем то, что вы хотите. Позвольте мне приспособить его...

combinations = [ [ {varName: val} for varName, val in zip(varNames, prod) ] for prod in it.product(*(variants[varName] for varName in varNames))]

теперь возвращается:

[[{'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'}]]

Voilà; -)

Ответ 2

combinations = [[{key: value} for (key, value) in zip(variants, values)] 
                for values in itertools.product(*variants.values())]

[[{'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'}]]

Ответ 3

Это то, что я использую:

from itertools import product

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

который применяется к вашему примеру, дает:

>>> 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'}]

Я считаю, что это более читаемо, чем те, которые были выше.

Кроме того, он возвращает итератор, например itertools.product, поэтому он оставляет его пользователю, создавать ли экземпляр списка или просто использовать значения по одному за раз.

Ответ 4

Я предполагаю, что вы хотите декартово произведение всех ключей? Итак, если у вас была другая запись "foo" со значениями [1, 2, 3], то у вас было бы 18 общих записей?

Сначала поместите значения в список, где каждая запись является одним из возможных вариантов в этом месте. В вашем случае мы хотим:

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

Для этого:

>>> stuff = []
>>> for k,v in variants.items():
    blah = []
    for i in v:
        blah.append({k:i})
    stuff.append(blah)


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

Далее мы можем использовать декартову функцию произведения, чтобы ее расширить...

>>> def cartesian_product(lists, previous_elements = []):
if len(lists) == 1:
    for elem in lists[0]:
        yield previous_elements + [elem, ]
else:
    for elem in lists[0]:
        for x in cartesian_product(lists[1:], previous_elements + [elem, ]):
            yield x


>>> list(cartesian_product(stuff))
[[{'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'}]]

Обратите внимание, что это не копирует dicts, поэтому все теги {'debug': 'on'} те же.