如何在Python中使for循环金字塔更简洁?

Yux*_*ang 62 python performance python-2.7 python-3.x

在固体力学中,我经常使用Python并编写如下所示的代码:

for i in range(3):
    for j in range(3):
        for k in range(3):
            for l in range(3):
                # do stuff
Run Code Online (Sandbox Code Playgroud)

我经常这样做,我开始怀疑是否有更简洁的方法来做到这一点.当前代码的缺点是:如果我遵守PEP8,那么我不能超过每行79个字符的限制,并且没有太多的空间,特别是如果这又是一个类的功能.

Kas*_*mvd 120

根据你想要做的事情,你可以使用itertools模块来最小化for循环(或zip).在这种情况下itertools.product,你将创建你用4循环完成的工作:

>>> list(product(range(3),repeat=4))
[(0, 0, 0, 0), (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 1, 0), (0, 0, 1, 1),
 (0, 0, 1, 2), (0, 0, 2, 0), (0, 0, 2, 1), (0, 0, 2, 2), (0, 1, 0, 0),
 (0, 1, 0, 1), (0, 1, 0, 2), (0, 1, 1, 0), (0, 1, 1, 1), (0, 1, 1, 2),
 (0, 1, 2, 0), (0, 1, 2, 1), (0, 1, 2, 2), (0, 2, 0, 0), (0, 2, 0, 1),
 (0, 2, 0, 2), (0, 2, 1, 0), (0, 2, 1, 1), (0, 2, 1, 2), (0, 2, 2, 0),
 (0, 2, 2, 1), (0, 2, 2, 2), (1, 0, 0, 0), (1, 0, 0, 1), (1, 0, 0, 2),
 (1, 0, 1, 0), (1, 0, 1, 1), (1, 0, 1, 2), (1, 0, 2, 0), (1, 0, 2, 1),
 (1, 0, 2, 2), (1, 1, 0, 0), (1, 1, 0, 1), (1, 1, 0, 2), (1, 1, 1, 0),
 (1, 1, 1, 1), (1, 1, 1, 2), (1, 1, 2, 0), (1, 1, 2, 1), (1, 1, 2, 2),
 (1, 2, 0, 0), (1, 2, 0, 1), (1, 2, 0, 2), (1, 2, 1, 0), (1, 2, 1, 1),
 (1, 2, 1, 2), (1, 2, 2, 0), (1, 2, 2, 1), (1, 2, 2, 2), (2, 0, 0, 0),
 (2, 0, 0, 1), (2, 0, 0, 2), (2, 0, 1, 0), (2, 0, 1, 1), (2, 0, 1, 2),
 (2, 0, 2, 0), (2, 0, 2, 1), (2, 0, 2, 2), (2, 1, 0, 0), (2, 1, 0, 1),
 (2, 1, 0, 2), (2, 1, 1, 0), (2, 1, 1, 1), (2, 1, 1, 2), (2, 1, 2, 0),
 (2, 1, 2, 1), (2, 1, 2, 2), (2, 2, 0, 0), (2, 2, 0, 1), (2, 2, 0, 2),
 (2, 2, 1, 0), (2, 2, 1, 1), (2, 2, 1, 2), (2, 2, 2, 0), (2, 2, 2, 1),
 (2, 2, 2, 2)]
Run Code Online (Sandbox Code Playgroud)

在您的代码中,您可以:

for i,j,k,l in product(range(3),repeat=4):
    #do stuff
Run Code Online (Sandbox Code Playgroud)

此函数等效于以下代码,但实际实现不会在内存中构建中间结果:

def product(*args, **kwds):
    # 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 = map(tuple, args) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)
Run Code Online (Sandbox Code Playgroud)

编辑:正如@ PeterE在评论中说的那样product()即使范围有不同的长度也可以使用:

product(range(3),range(4),['a','b','c'] ,some_other_iterable)
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,这是要走的路.至少如果循环是如OP中所示的基本循环.这个方案也可以处理不同长度的范围(实际上它可以处理任意迭代):`product(range(3),range(4),['a','b','c'],some_other_iterable)`.如果嵌套循环比这更复杂(例如包含一些if/else逻辑),那么定义on generator函数是下一个最好的事情.在这里,我更喜欢@ SergeBallesta的变体而不是@L3viathan的发生器,因为它简单易懂. (7认同)
  • 这是进入这种情况的唯一方法,只需要一些FP概念和代码就可以更快速地获得更优雅/简洁. (2认同)

FMc*_*FMc 15

使用的想法itertools.product很好.这是一种更通用的方法,可以支持不同大小的范围.

from itertools import product

def product_of_ranges(*ns):
    for t in product(*map(range, ns)):
        yield t

for i, j, k in product_of_ranges(4, 2, 3):
    # do stuff
Run Code Online (Sandbox Code Playgroud)


Ser*_*sta 13

它不会更简洁,因为它会花费你一个发电机功能,但至少你不会被PEP8打扰:

def tup4(n):
    for i in range(n):
        for j in range(n):
            for k in range(n):
                for l in range(n):
                    yield (i, j, k, l)

for (i, j, k, l) in tup4(3):
    # do your stuff
Run Code Online (Sandbox Code Playgroud)

(在python 2.x中你应该使用xrange而不是range在生成器函数中)

编辑:

当已知金字塔的深度时,上述方法应该是正确的.但您也可以在没有任何外部模块的情况下制作通用生成器:

def tup(n, m):
    """ Generate all different tuples of size n consisting of integers < m """
    l = [ 0 for i in range(n)]
    def step(i):
        if i == n : raise StopIteration()
        l[i] += 1
        if l[i] == m:
            l[i] = 0
            step(i+ 1)
    while True:
        yield tuple(l)
        step(0)

for (l, k, j, i) in tup(4, 3):
    # do your stuff
Run Code Online (Sandbox Code Playgroud)

(我用过,(l, k, j, i)因为在上面的发电机中,第一个指数先变化)


L3v*_*han 8

这相当于:

for c in range(3**4):
    i = c // 3**3 % 3
    j = c // 3**2 % 3
    k = c // 3**1 % 3
    l = c // 3**0 % 3
    print(i,j,k,l)
Run Code Online (Sandbox Code Playgroud)

如果您一直这样做,请考虑使用通用生成器:

def nestedLoop(n, l):
    return ((tuple((c//l**x%l for x in range(n-1,-1,-1)))) for c in range(l**n))

for (a,b,c,d) in nestedLoop(4,3):
    print(a,b,c,d)
Run Code Online (Sandbox Code Playgroud)