40

这个问题已经困扰我一段时间了。有没有更简单的方法for在 python 中编写嵌套循环?例如,如果我的代码是这样的:

  for y in range(3):
    for x in range(3):
      do_something()
      for y1 in range(3):
        for x1 in range(3):
          do_something_else()

会有更简单的方法吗?我知道这段代码有效,但是当你像我一样缩进而不是使用 2 个空格时,它可能会成为一个问题。

哦,在示例中,只有 4 个嵌套for循环使事情变得更容易。

4

11 回答 11

59

如果您像示例中那样经常迭代笛卡尔积,您可能需要研究Python 2.6 的 itertools.product ——或者如果您使用的是早期的 Python,请编写自己的。

from itertools import product
for y, x in product(range(3), repeat=2):
  do_something()
  for y1, x1 in product(range(3), repeat=2):
    do_something_else()
于 2009-08-14T23:51:35.923 回答
14

这在循环多维空间时相当常见。我的解决方案是:

xy_grid = [(x, y) for x in range(3) for y in range(3)]

for x, y in xy_grid:
    # do something
    for x1, y1 in xy_grid:
        # do something else
于 2009-08-14T23:53:34.517 回答
10

当面对这种程序逻辑时,我可能会将循环序列分解为两个或多个单独的函数。

Python 中的另一种技术是尽可能使用列表推导,而不是循环。

于 2009-08-14T23:36:03.223 回答
8

假设每个循环都有某种独立的含义,将它们分解为命名函数:

def do_tigers():
    for x in range(3):
        print something

def do_lions():
    do_lionesses()
    for x in range(3):
        do_tigers()

def do_penguins():
    for x in range(3):
        do_lions()

..etc.

我也许可以选择更好的名字。8-)

于 2009-08-14T23:36:59.230 回答
6

从技术上讲,您可以使用itertools.product得到 N 个序列的笛卡尔积,并对其进行迭代:

 for y, x, y1, x1 in itertools.product(range(3), repeat=4):
   do_something_else()

但我认为这实际上不会为您赢得任何可读性。

于 2009-08-14T23:51:54.503 回答
4

Python 迭代器,尤其是生成器,正是为了对原本复杂的循环进行良好的重构而存在。当然,很难从一个简单的例子中得到一个抽象,但是假设3需要是一个参数(也许整个range(3)应该是?),并且您调用的两个函数需要一些作为循环变量的参数,您可以重构代码:

  for y in range(3):
    for x in range(3):
      do_something(x, y)
      for y1 in range(3):
        for x1 in range(3):
          do_something_else(x, y, x1, y1)

进入,例如:

def nestloop(n, *funcs):
  head = funcs[0]
  tail = funcs[1:]
  for y in range(n):
    for x in range(n):
      yield head, x, y
      if tail:
        for subtup in nestloop(n, *tail):
           yield subtup[:1] + (x, y) + subtup[1:]

for funcandargs in nestloop(3, do_something, do_something_else):
  funcandargs[0](*funcandargs[1:])

确切的重构类型无疑需要根据您的确切目的进行调整,但迭代器(通常实际上只是简单的生成器)提供非常好的循环重构的一般观点仍然存在——所有循环逻辑都在生成器内部,并且应用程序级代码留下了简单的for循环和对 for 循环中产生的项目的实际应用程序相关处理。

于 2009-08-15T00:59:33.683 回答
3

我个人的观点是,如果你有 6 个嵌套循环,你可能会做错事......

也就是说,功能分解是您正在寻找的。重构一些循环在单独的函数调用中发生,然后调用这些函数。

于 2009-08-14T23:36:32.083 回答
3

从您的代码看来,您希望对 x 和 y 在 0..2 范围内的每一对可能的点执行操作。

要做到这一点:

for x1,y1,x2,y2 in itertools.product(range(3), repeat=4):
    do_something_with_two_points(x1,y1,2,y2)

该操作do_something_with_two_points将被调用 81 次 - 为每个可能的点组合调用一次。

于 2009-08-14T23:57:37.737 回答
2

您是否查看过列表理解?

就像是:

[do_something() for x in range(3) for y in range(3)]
于 2009-08-14T23:37:05.187 回答
2

这种方式看起来非常简单明了。你是说你想推广到多层循环......你能举一个真实的例子吗?

我能想到的另一个选择是使用一个函数来生成参数,然后在循环中应用它们

def generate_params(n):
    return itertools.product(range(n), range(n))

for x,y in generate_params(3):
    do_something()
于 2009-08-14T23:37:45.407 回答
1

您还可以使用map() 函数

于 2009-08-14T23:47:07.477 回答