前文要实现python同时启动多个不同参数脚本(这个可能过段时间再分享出来),但是前提要解决一个问题,如何根据不确定的参数,有序生成参数列表;
比如我们的参数字典为:
params_dict = {
'lr': [2, 3, 4],
"batch": [10, 20, 30],
"epoch": [9, 8, 7],
}
我们每次拿到的字典,可能有3个key,也有可能是2个,每个key对应的value的值也不确定有多少个值;
那么我们不能简单的整一个for循环,还是3个for循环,去生成所有参数列表;
我自己晚上想了好久,好几种方案都没有实现的优雅;
比如,先计算出所有的可能参数数目,即param_num = value_num1value_num2value_numn.
然后直接for num in range(param_num),对每一个num,进行再转换成每一个key的序号,花里胡哨的,还要设计乱进制的转换,太复杂了,这一顿操作下来,好几个函数,几十行代码都搞不定。
因此我只好求助百度:python多层for循环
看到了这样的一篇博客,python 多层for循环嵌套的优化方法
里面有这样一个函数操作:
一番搜索后,发现了itertools.product(A, B)这个函数,这个函数会返回A、B中的元素的笛卡尔积的元组,似乎满足条件,于是上面的脚本改成了下面的样子
import itertools
for i in itertools.product(a1,a2,a3,a4,a5):
if i[0]<i[1]<i[2]<i[3]<i[4]:
print(i)
讲道理没看懂,但是简单debug发现,确实可以实现我的功能,因此继续深入的检索。
找到了官方文档:
https://docs.python.org/3/library/itertools.html#itertools.product
nice,找到了这个函数的介绍,最底下,会翻译这个函数的功能。
先上我的代码实现。
问题:
输入为不定key的字典;
例如:
params_dict = {
'lr': [2, 3, 4],
"batch": [10, 20, 30],
"epoch": [9, 8, 7],
}
输出:
每个key的排列组合列表,例如:
params_list: [[2, 10, 9], [2, 10, 8], [2, 10, 7], [2, 20, 9], [2, 20, 8], [2, 20, 7], [2, 30, 9], [2, 30, 8], [2, 30, 7], [3, 10, 9], [3, 10, 8], [3, 10, 7], [3, 20, 9], [3, 20, 8], [3, 20, 7], [3, 30, 9], [3, 30, 8], [3, 30, 7], [4, 10, 9], [4, 10, 8], [4, 10, 7], [4, 20, 9], [4, 20, 8], [4, 20, 7], [4, 30, 9], [4, 30, 8], [4, 30, 7]]
一行代码实现转换~
import itertools
params_dict = {
'lr': [2, 3, 4],
"batch": [10, 20, 30],
"epoch": [9, 8, 7],
}
params_list = [list(value) for value in itertools.product(*params_dict.values())]
print("params_list:", params_list)
关于python中这个星号*的使用和含义,可以参考我的这篇博客:
Python中的和**(一文搞懂Python的传参)
itertools.product(iterables, repeat=1)¶
输入的是iterables,得加一个星号,原iterables是一个列表,可以迭代的那种。
Cartesian product of input iterables.
输出是可迭代对象的笛卡尔乘积,好吧我不太懂笛卡尔乘积;
Roughly equivalent to nested for-loops in a generator expression. For example, product(A, B) returns the same as ((x, y) for x in A for y in B).
大致相当于生成器表达式中的嵌套for循环。例如,product(A, B)返回的结果与
[(x, y) For x in A For y in B]相同。
The nested loops cycle like an odometer with the rightmost element advancing on every iteration. This pattern creates a lexicographic ordering so that if the input’s iterables are sorted, the product tuples are emitted in sorted order.
嵌套的循环就像里程计一样循环,在每次迭代中最右边的元素前进。这个模式创建了一个字典顺序,这样如果输入的可迭代对象已经排序,乘积元组就会按照排序的顺序产生。
To compute the product of an iterable with itself, specify the number of repetitions with the optional repeat keyword argument. For example, product(A, repeat=4) means the same as product(A, A, A, A).
若要计算可迭代对象与自身的乘积,请使用可选的repeat关键字参数指定重复次数。例如,product(A, repeat=4)与product(A, A, A, A)相同。
This function is roughly equivalent to the following code, except that the actual implementation does not build up intermediate results in memory:
这个函数与下面的代码大致相同,除了实际的实现没有在内存中建立中间结果:
def product(*args, repeat=1):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools = [tuple(pool) for pool in args] * repeat
result = [[]]
for pool in pools:
# 这里面有一个点比较重要,就是虽然pool可能有很多个,但是最终的result最终就是一个二维的,因此用一个两层for循环就能解决~
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
Before product() runs, it completely consumes the input iterables, keeping pools of values in memory to generate the products. Accordingly, it is only useful with finite inputs.
在product()运行之前,它将完全消耗输入的可迭代对象,并在内存中保留值池来生成产品。因此,它只对有限输入有用。
python中yield的用法详解——最简单,最清晰的解释