列表词典的笛卡儿积

我正在写一些代码来测试一些输入参数的笛卡儿积。

我看过 itertools,但它的 product功能并不完全是我想要的。有没有一种简单明了的方法可以获取一个字典,其中包含任意数量的键 还有和每个值中任意数量的元素,然后生成一个带有下一个排列的字典?

输入:

options = {"number": [1,2,3], "color": ["orange","blue"] }
print list( my_product(options) )

输出示例:

[ {"number": 1, "color": "orange"},
{"number": 1, "color": "blue"},
{"number": 2, "color": "orange"},
{"number": 2, "color": "blue"},
{"number": 3, "color": "orange"},
{"number": 3, "color": "blue"}
]
24662 次浏览

顺便说一句,这不是排列。排列是列表的重新排列。这是列表中可能的选择的枚举。

编辑: 在记起它被称为笛卡儿积之后,我想到了这个:

import itertools
options = {"number": [1,2,3], "color": ["orange","blue"] }
product = [x for x in apply(itertools.product, options.values())]
print([dict(zip(options.keys(), p)) for p in product])

好的,谢谢@dfan 告诉我我找错地方了。我现在明白了:

from itertools import product
def my_product(inp):
return (dict(zip(inp.keys(), values)) for values in product(*inp.values())

EDIT : 在多年的 Python 经验之后,我认为更好的解决方案是接受 kwargs而不是输入字典; 调用样式更类似于原来的 itertools.product。另外,我认为编写一个生成器函数,而不是返回生成器表达式的函数,可以使代码更加清晰。所以:

def product_dict(**kwargs):
keys = kwargs.keys()
vals = kwargs.values()
for instance in itertools.product(*vals):
yield dict(zip(keys, instance))

如果你需要传递一个结论,list(product_dict(**mydict))。使用 kwargs而不是任意输入类的一个显著变化是,它阻止键/值被排序,至少在 Python 3.6之前是这样。

# I would like to do
keys,values = options.keys(), options.values()
# but I am not sure that the keys and values would always
# be returned in the same relative order. Comments?
keys = []
values = []
for k,v in options.iteritems():
keys.append(k)
values.append(v)


import itertools
opts = [dict(zip(keys,items)) for items in itertools.product(*values)]

结果出来了

opts = [
{'color': 'orange', 'number': 1},
{'color': 'orange', 'number': 2},
{'color': 'orange', 'number': 3},
{'color': 'blue', 'number': 1},
{'color': 'blue', 'number': 2},
{'color': 'blue', 'number': 3}
]

Python 3版本的 赛斯的回答

import itertools


def dict_product(dicts):
"""
>>> list(dict_product(dict(number=[1,2], character='ab')))
[{'character': 'a', 'number': 1},
{'character': 'a', 'number': 2},
{'character': 'b', 'number': 1},
{'character': 'b', 'number': 2}]
"""
return (dict(zip(dicts, x)) for x in itertools.product(*dicts.values()))