在Python中重新执行for循环迭代

21

Python 是否有类似其他语言的 "redo" 语句呢?

("redo" 语句是一种会影响循环行为的语句,就像 "break" 或 "continue" 一样——它跳到最内层循环的开头并重新开始执行。)


有很多种方法可以实现这个功能。首先,你可以使用一个while循环,在某次评估后重新设置计数器/条件。 - miradulo
4
从未听说过这样的事情。听起来很像goto命令。 - Christopher Schneider
1
@ChristopherSchneider:Perl使用它(不管值不值得)。想象一个不执行循环前进步骤的“continue”。由于它与循环本身绑定,因此在道德上与continuebreak没有区别;如果您接受它们作为不仅仅是“goto”的东西,则redo并不更糟糕(或更好)。 - ShadowRanger
7个回答

10
不,Python没有直接支持redo。一个选项是使用嵌套循环,但这种方法可能会有些糟糕,例如:
for x in mylist:
    while True:
        ...
        if shouldredo:
            continue  # continue becomes equivalent to redo
        ...
        if shouldcontinue:
            break     # break now equivalent to continue on outer "real" loop
        ...
        break  # Terminate inner loop any time we don't redo

但这意味着在“redo”块内部无法中断外部循环,除非使用异常、标志变量或将整个事情打包为函数。

或者,您可以使用直接的while循环来复制for循环为您完成的操作,明确地创建和推进迭代器。它有自己的问题(continue默认情况下有效地是redo,您必须显式地推进迭代器以进行“真正”的continue),但它们并不可怕(只要您注释使用continue以清楚地表明您打算使用redo vs. continue,以避免混淆维护者)。要允许redo和其他循环操作,您需要执行以下操作:

# Create guaranteed unique sentinel (can't use None since iterator might produce None)
sentinel = object()
iterobj = iter(mylist)  # Explicitly get iterator from iterable (for does this implicitly)
x = next(iterobj, sentinel)  # Get next object or sentinel
while x is not sentinel:     # Keep going until we exhaust iterator
    ...
    if shouldredo:
        continue
    ...
    if shouldcontinue:
        x = next(iterobj, sentinel)  # Explicitly advance loop for continue case
        continue
    ...
    if shouldbreak:
        break
    ...
    # Advance loop
    x = next(iterobj, sentinel)

上述操作也可以使用try/except StopIteration:来代替带有sentinel的两个参数的next,但是将整个循环包装在其中会冒险捕获其他StopIteration源,并且在有限的范围内正确处理内部和外部next调用会非常丑陋(比sentinel方法更糟糕)。

这应该是被接受的答案。重新做一遍不太符合Python的风格,而使用标志来模拟for循环并在必要时进行重做将更容易让其他人理解。谢谢。 - ddelange

8
不会的,我建议使用 while 循环,并将您的检查变量重置为初始值。
count = 0
reset = 0
while count < 9:
   print 'The count is:', count
   if not someResetCondition:
       count = count + 1

使用 reset 不能复制其他语言中 redo 的功能。redocontinue,但没有循环增量/前进步骤,但它不会从头重新开始循环;您只需使 count 增量可选,而不是有一个 reset 变量。 - ShadowRanger
啊,我误解了你最初的说法“它在开始时跳跃”,以为是指初始点,而不仅仅是循环的顶部。我会修改我的回答。 - cmaynard
我不是 OP,那是他们的初始声明,不是我的。我承认我可能会误读,但我手头唯一知道 redo 的语言是 Perl,并且它的行为是这样的。注意:如果你要替换 for count in range(10):,编辑后的代码是可以的,但它并不能很好地适用于任意可迭代对象;我在 我的回答 中的第二个代码示例是完全通用的版本。 - ShadowRanger

1

我在学习Perl时遇到了同样的问题,然后我找到了这个页面。

跟随Perl书籍:

my @words = qw(fred barney pebbles dino wilma betty);
my $error = 0;

my @words = qw(fred barney pebbles dino wilma betty);
my $error = 0;

foreach (@words){
    print "Type the word '$_':";
    chomp(my $try = <STDIN>);
    if ($try ne $_){
        print "Sorry - That's not right.\n\n";
        $error++;
        redo;
    }
}

如何在Python中实现它?请按照以下代码进行操作:
tape_list=['a','b','c','d','e']

def check_tape(origin_tape):
    errors=0
    while True:
        tape=raw_input("input %s:"%origin_tape)
        if tape == origin_tape:
            return errors
        else:
            print "your tape %s,you should tape %s"%(tape,origin_tape)
            errors += 1
            pass

all_error=0
for char in tape_list:
    all_error += check_tape(char)
print "you input wrong time is:%s"%all_error

Python没有"redo"语法,但是我们可以在某些函数中使用'while'循环,直到我们迭代列表并得到想要的结果。


1

虽然不是很复杂,但易于阅读。使用 while 和一个在循环结束时的递增。因此,在其中任何 continue 的影响下都会重新执行。示例:重新做每个3的倍数:

redo = True # To ends redo condition in this sample only
i = 0
while i<10:
   print(i, end='')
   if redo and i % 3 == 0:
      redo = False # To not loop indifinively in this sample
      continue # Redo
   redo = True
   i += 1

结果:00123345667899

1

这是我使用迭代器的解决方案:

class redo_iter(object):
    def __init__(self, iterable):
        self.__iterator = iter(iterable)
        self.__started = False
        self.__redo = False
        self.__last = None
        self.__redone = 0
    def __iter__(self):
        return self
    def redo(self):
        self.__redo = True
    @property
    def redone(self):
        return self.__redone
    def __next__(self):
        if not (self.__started and self.__redo):
            self.__started = True
            self.__redone = 0
            self.__last = next(self.__iterator)
        else:
            self.__redone += 1
        self.__redo = False
        return self.__last


# Display numbers 0-9.
# Display 0,3,6,9 doubled.
# After a series of equal numbers print --
iterator = redo_iter(range(10))
for i in iterator:
    print(i)
    if not iterator.redone and i % 3 == 0:
        iterator.redo()
        continue
    print('---')
  • 需要显式使用 continue
  • redone 是额外的功能
  • 对于Python2,请使用def next(self)而不是def __next__(self)
  • 在循环之前需要定义iterator

0

这里提供了一个Python 3.8+的解决方案,因为现在我们有了 :=运算符

for key in mandatory_attributes:  # example with a dictionary
    while not (value := input(f"{key} (mandatory): ")):
        print("You must enter a value")

    mandatory_attributes[key] = value

0

Python 中没有重做功能。 一个非常易懂的解决方案如下:

for x in mylist:
    redo = True
    while redo:
        redo = False

        If should_redo:
            redo = True

代码已经足够清晰,不需要添加注释。

Continue 语句将会像在 for 循环中一样工作。

但是 break 语句无法使用,这个 solution 可以让 break 语句可用,但是代码会变得不太清晰。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接