如何在 Python 中使 for 循环金字塔更简洁?
How can I make a for-loop pyramid more concise in Python?
在固体力学中,我经常使用 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 个字符的限制,并且没有太多 space 剩余,特别是如果这又是class.
的函数
通过使用嵌套的 for 循环,您基本上是在尝试创建所谓的输入迭代的(笛卡尔)积,这就是 itertools
模块中的 product
函数,是为了.
>>> 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),
...
在你的代码中你可以这样做:
for i,j,k,l in product(range(3),repeat=4):
#do stuff
根据 python 文档,“此函数大致等同于以下代码,只是实际实现不会在内存中建立中间结果:”
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:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
这等价于:
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)
它不会更简洁,因为它会花费你一个生成器函数,但至少你不会被 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)
因为在上面的生成器中,第一个索引先变化)
使用 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
在固体力学中,我经常使用 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 个字符的限制,并且没有太多 space 剩余,特别是如果这又是class.
通过使用嵌套的 for 循环,您基本上是在尝试创建所谓的输入迭代的(笛卡尔)积,这就是 itertools
模块中的 product
函数,是为了.
>>> 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),
...
在你的代码中你可以这样做:
for i,j,k,l in product(range(3),repeat=4):
#do stuff
根据 python 文档,“此函数大致等同于以下代码,只是实际实现不会在内存中建立中间结果:”
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:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
这等价于:
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)
它不会更简洁,因为它会花费你一个生成器函数,但至少你不会被 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)
因为在上面的生成器中,第一个索引先变化)
使用 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