642

次のコードが与えられます(それは機能しません):

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": break 2 #this doesn't work :(
        if ok.lower() == "n": break
    #do more processing with menus and stuff

これを機能させる方法はありますか?または、入力ループから抜け出すために1つのチェックを実行し、次に、ユーザーが満足している場合は、外側のループをチェックインしてすべてをまとめてブレークアウトする必要がありますか?

4

33 に答える 33

655

私の最初の本能は、ネストされたループを関数にリファクタリングし、それを使用returnして抜け出すことです。

于 2008-10-10T00:25:05.480 に答える
400

短い別のアプローチがあります。欠点は、外側のループしか壊せないことですが、それがまさにあなたが望むものである場合もあります。

for a in xrange(10):
    for b in xrange(20):
        if something(a, b):
            # Break the inner loop...
            break
    else:
        # Continue if the inner loop wasn't broken.
        continue
    # Inner loop was broken, break the outer.
    break

これは、次のように説明されているfor / else構文を使用します。Pythonがforループとwhileループの後に「else」を使用するのはなぜですか?

重要な洞察:外側のループが常に壊れているように見えるだけです。しかし、内側のループが壊れなければ、外側のループも壊れません。

continueステートメントはここでの魔法です。for-else句にあります。定義上、内部ブレークがない場合に発生します。そのような状況でcontinueは、外側の切れ目をきちんと回避します。

于 2010-06-30T14:15:59.533 に答える
170

PEP 3136は、ラベル付きの中断/継続を提案しています。Guidoは、「この機能を要求するほど複雑なコードは非常にまれ」という理由で、これを却下しました。ただし、PEP にはいくつかの回避策 (例外手法など) が記載されていますが、Guido は return を使用するためのリファクタリングがほとんどの場合より簡単になると感じています。

于 2008-10-10T03:50:44.317 に答える
145

まず、通常のロジックが役に立ちます。

何らかの理由で終了条件を解決できない場合、例外はフォールバック プランです。

class GetOutOfLoop( Exception ):
    pass

try:
    done= False
    while not done:
        isok= False
        while not (done or isok):
            ok = get_input("Is this ok? (y/n)")
            if ok in ("y", "Y") or ok in ("n", "N") : 
                done= True # probably better
                raise GetOutOfLoop
        # other stuff
except GetOutOfLoop:
    pass

この特定の例では、例外は必要ない場合があります。

一方、文字モードのアプリケーションでは、「Y」、「N」、および「Q」オプションがよくあります。「Q」オプションの場合、即時終了が必要です。それはもっと例外的です。

于 2008-10-10T00:11:37.910 に答える
60

通常、関数へのリファクタリングがこの種の状況に最適なアプローチであることに同意する傾向がありますが、ネストされたループから本当に抜け出す必要がある場合は、@S.Lott が説明した例外を発生させるアプローチの興味深い変形を次に示します。Python のwithステートメントを使用して、例外の発生を少し見やすくしています。以下を使用して、新しいコンテキスト マネージャーを定義します (これは一度だけ行う必要があります)。

from contextlib import contextmanager
@contextmanager
def nested_break():
    class NestedBreakException(Exception):
        pass
    try:
        yield NestedBreakException
    except NestedBreakException:
        pass

これで、このコンテキスト マネージャーを次のように使用できます。

with nested_break() as mylabel:
    while True:
        print "current state"
        while True:
            ok = raw_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": raise mylabel
            if ok == "n" or ok == "N": break
        print "more processing"

利点: (1) ややクリーン (明示的な try-except ブロックがない) であり、(2) ;を使用するたびにカスタムビルドのExceptionサブクラスを取得します。毎回nested_break独自のサブクラスを宣言する必要はありません。Exception

于 2010-07-03T15:50:53.507 に答える
55

「ループ ブレーカー」として使用する新しい変数を導入します。最初に何か (False、0 など) を割り当ててから、外側のループ内で、それを中断する前に値を別のもの (True、1、...) に変更します。ループが終了したら、「親」ループでその値をチェックします。実演してみましょう:

breaker = False #our mighty loop exiter!
while True:
    while True:
        if conditionMet:
            #insert code here...
            breaker = True 
            break
    if breaker: # the interesting part!
        break   # <--- !

無限ループがある場合、これが唯一の解決策です。他のループの実行は、実際にははるかに高速です。これは、ネストされたループが多数ある場合にも機能します。すべてを終了することも、いくつかを終了することもできます。限りない可能性!これが役に立ったことを願っています!

于 2011-07-03T18:15:55.093 に答える
46

まず、入力を取得して検証するプロセスを関数にすることも検討してください。その関数内で、正しい場合は値を返すだけで、そうでない場合はwhileループで回転し続けることができます。これにより、解決した問題が本質的に回避され、通常はより一般的なケース (複数のループから抜け出す) に適用できます。この構造をコードに絶対に保持する必要があり、実際に簿記ブール値を扱いたくない場合...

次のようにgotoを使用することもできます(ここからエイプリル フール モジュールを使用します)。

#import the stuff
from goto import goto, label

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": goto .breakall
        if ok == "n" or ok == "N": break
    #do more processing with menus and stuff
label .breakall

私は知っています、私は知っています、「あなたはgotoを使うべきではありません」とすべてのことを知っていますが、これはこのような奇妙な場合にうまく機能します。

于 2008-10-10T00:12:38.533 に答える
20
keeplooping=真
キープループ中:
    #やりたいこと
    キープループ中:
          #他のことをする
          終了した場合():
              キープループ=False

またはそのようなもの。内側のループで変数を設定し、内側のループが終了した直後に外側のループで変数をチェックし、必要に応じて中断することができます。エイプリルフールのジョークモジュールを使用しても構わないのであれば、GOTOメソッドが好きです-Pythonicではありませんが、理にかなっています。

于 2008-10-10T00:29:37.640 に答える
13

これは最も美しい方法ではありませんが、私の意見では、これが最善の方法です。

def loop():
    while True:
    #snip: print out current state
        while True:
            ok = get_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": return
            if ok == "n" or ok == "N": break
        #do more processing with menus and stuff

ここでも再帰を使用して何かを解決できると確信していますが、それが適切なオプションであるかどうかはわかりません。

于 2008-10-10T01:41:42.643 に答える
11

そして、2 つの条件が true の場合にループを続けないのはなぜでしょうか? これはよりpythonicな方法だと思います:

dejaVu = True

while dejaVu:
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y" or ok == "n" or ok == "N":
            dejaVu = False
            break

ではない?

ではごきげんよう。

于 2012-11-06T14:02:32.670 に答える
8

ループ ロジックを反復子に分解して、ループ変数を生成し、完了すると戻ります。これは、画像がなくなるか、配置する場所がなくなるまで、画像を行/列にレイアウトする単純なものです。

def it(rows, cols, images):
    i = 0
    for r in xrange(rows):
        for c in xrange(cols):
            if i >= len(images):
                return
            yield r, c, images[i]
            i += 1 

for r, c, image in it(rows=4, cols=4, images=['a.jpg', 'b.jpg', 'c.jpg']):
    ... do something with r, c, image ...

これには、複雑なループ ロジックと処理を分割できるという利点があります...

于 2010-04-12T11:33:15.763 に答える
4

while ... elsePython構造には、あまりコードを変更/追加せずに二重ブレークをシミュレートするために使用できる隠れたトリックがあります。基本的に、while条件が false の場合、elseブロックがトリガーされます。例外もcontinue、ブロックbreakのトリガーもありません。else詳細については、「Python while ステートメントの Else 句」への回答、またはwhile (v2.7) の Python doc を参照してください。

while True:
    #snip: print out current state
    ok = ""
    while ok != "y" and ok != "n":
        ok = get_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N":
            break    # Breaks out of inner loop, skipping else

    else:
        break        # Breaks out of outer loop

    #do more processing with menus and stuff

唯一の欠点は、二重ブレーク条件を条件に移動するwhile(またはフラグ変数を追加する) 必要があることです。これのバリエーションは、ループの完了後にブロックがトリガーされるforループにも存在します。else

于 2015-10-14T09:29:41.420 に答える
3

反復を単一レベルのループに減らす別の方法は、Python リファレンスでも指定されているジェネレーターを使用することです。

for i, j in ((i, j) for i in A for j in B):
    print(i , j)
    if (some_condition):
        break

ループの任意の数のレベルにスケールアップできます

欠点は、1 つのレベルだけを壊すことができなくなったことです。それはすべてまたは何もありません。

もう 1 つの欠点は、while ループでは機能しないことです。私はもともと、この回答をPython に投稿したかった - すべてのループから「break」しましたが、残念ながら、この回答の複製として閉じられています

于 2016-12-01T18:10:03.337 に答える
2
# this version uses a level counter to choose how far to break out

break_levels = 0
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_levels = 1        # how far nested, excluding this break
            break
        if ok == "n" or ok == "N":
            break                   # normal break
    if break_levels:
        break_levels -= 1
        break                       # pop another level
if break_levels:
    break_levels -= 1
    break

# ...and so on
于 2013-06-13T16:53:56.533 に答える
2

ここに来る理由は、次のような外側のループと内側のループがあったからです。

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

  do some other stuff with x

ご覧のとおり、実際には次の x には移動しませんが、代わりに次の y に移動します。

これを解決するために私が見つけたのは、代わりに配列を2回実行することでした:

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

for x in array:
  do some other stuff with x

これはOPの質問の特定のケースであることは知っていますが、物事をシンプルに保ちながら、誰かが問題について別の考え方をするのに役立つことを期待して投稿しています。

于 2012-08-15T18:12:49.110 に答える
-3

前と同じですが、よりコンパクトです。(ブール値は単なる数値です)

breaker = False #our mighty loop exiter!
while True:
    while True:
        ok = get_input("Is this ok? (y/n)")
        breaker+= (ok.lower() == "y")
        break

    if breaker: # the interesting part!
        break   # <--- !
于 2012-06-07T11:12:22.363 に答える