我怎样才能使Python for循环金字塔更简洁?

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

for i in range(3): for j in range(3): for k in range(3): for l in range(3): # do stuff 

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

根据你想做什么,你可以使用itertools模块来最小化for循环(或zip )。在这种情况下, itertools.product将创build你已经完成了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)] 

在你的代码中,你可以这样做:

 for i,j,k,l in product(range(3),repeat=4): #do stuff 

这个函数等同于下面的代码,除了实际的实现不会在内存中build立中间结果:

 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) 

编辑 :由于@彼得E说在评论product()可以使用,即使范围有不同的长度:

 product(range(3),range(4),['a','b','c'] ,some_other_iterable) 

使用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 

它不会更简洁,因为它会花费你一个发电机function,但至less你不会被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 

(在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 

(我使用(l, k, j, i)因为在上面的发生器中,第一个索引首先变化)

这相当于:

 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) 

如果你一直在做这个,那么考虑使用一个通用的发生器:

 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)