python – 使用Itertools的等效嵌套循环结构

Python的succint语法通过其电池允许详细的代码行以可读的一行表示.请考虑以下示例

====================================================|
for a in range(3):                                  |
    for b in range(3):                              |
        for c in range(3):                          |
            print (a,b,c),                          |
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -|
for e in product(range(3), repeat=3):               |
    print e,                                        |
====================================================|
for a in range(3):                                  |
    for b in range(a , 3):                          |
        for c in range(b , 3):                      |
            print (a,b,c),                          |
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -|
for e in combinations_with_replacement(range(3), 3):|
    print e,                                        |
====================================================|
for a in range(3):                                  |
    for b in range(a + 1, 3):                       |
        for c in range(b + 1, 3):                   |
            print (a,b,c),                          |
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -|
for e in combinations(range(3), 3):                 |
    print e,                                        |
====================================================|
for a in range(3):                                  |
    for b in range(3):                              |
        for c in range(3):                          |
            if len(set([a,b,c])) == 3:              |
                print (a,b,c),                      |
-  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -|
for e in permutations(range(3)):                    |
    print e,                                        |
====================================================|

我最后得到一个深嵌套的依赖循环我试图简洁地表达但失败了

循环的结构如下

for a in A():
    for b in B(a):
        for c in C(b):
            foo(a,b,c)

这种结构能用等效的itertools符号表示吗?

解决方法:

没有确切的itertools解决方案,但itertools函数的简单组合就足够了:

def chain_imap_accumulate(seq, f):
    def acc_f(x):
        for n in f(x[-1]):
            yield x + (n,)
    return chain.from_iterable(imap(acc_f, seq))

def accumulative_product(*generators):
    head, tail = generators[0], generators[1:]
    head = imap(tuple, head())
    return reduce(chain_imap_accumulate, tail, head)

快速测试.定义:

from itertools import chain, imap, izip
chain_ = chain.from_iterable

def A():
    yield 'A'
    yield 'B'

def B(x):
    yield int(x, 16)
    yield int(x, 16) + 1

def C(x):
    yield str(x) + 'Z'
    yield str(x) + 'Y'

结果如下:

>>> list(accumulative_product(A, B, C))
[('A', 10, '10Z'), ('A', 10, '10Y'), 
 ('A', 11, '11Z'), ('A', 11, '11Y'), 
 ('B', 11, '11Z'), ('B', 11, '11Y'), 
 ('B', 12, '12Z'), ('B', 12, '12Y')]

几乎所有的复杂性都来自于输入的积累,正如上面代码的快速“推导”所示.最终的(c)值可以使用几个嵌套的itertools构造生成:

>>> list(chain_(imap(C, chain_(imap(B, (A()))))))
['10Z', '10Y', '11Z', '11Y', '11Z', '11Y', '12Z', '12Y']

这可以通过reduce来概括.要使用reduce,chain_imap不能使用标准的imap参数顺序.它必须被交换:

def chain_imap(seq, f):
    return chain.from_iterable(imap(f, seq))

这给出了相同的结果:

>>> list(reduce(chain_imap, [B, C], A()))
['10Z', '10Y', '11Z', '11Y', '11Z', '11Y', '12Z', '12Y']

最后一项任务是累积初始值,以便您可以访问a,b和c.这需要一些思考才能正确,但实现相当简单 – 我们只需将f转换为忽略所有输入值但最后一个的函数,并将新值附加到完整输入:

def chain_imap_accumulate(seq, f):
    def acc_f(x):
        for n in f(x[-1]):
            yield x + (n,)
    return chain.from_iterable(imap(acc_f, seq))

这要求将第一个输入包装在元组中,因此我们使用元组映射A:

>>> list(reduce(chain_imap_accumulate, [B, C], imap(tuple, A())))
[('A', 10, '10Z'), ('A', 10, '10Y'), 
 ('A', 11, '11Z'), ('A', 11, '11Y'), 
 ('B', 11, '11Z'), ('B', 11, '11Y'), 
 ('B', 12, '12Z'), ('B', 12, '12Y')]

为清晰起见,重写上述内容,并在此答案的顶部显示代码.

顺便说一下,使用genex可以更简洁地重写chain_imap_accumulate.这可以与更短版本的accumulative_product结合使用,以获得非常紧凑的定义(如果您对此类事物感兴趣).这也恰好完全消除了itertools依赖:

def chain_map_accumulate(seq, f):
    return (x + (n,) for x in seq for n in f(x[-1]))

def accumulative_product2(*gens):
    return reduce(chain_map_accumulate, gens[1:], (tuple(x) for x in gens[0]()))
上一篇:Python itertools 操作迭代对象


下一篇:python – Numpy相当于itertools.product