web-dev-qa-db-ja.com

なぜpythonはforとwhileのループの後に 'else'を使うのですか?

私はこの構文がどのように機能するのか理解しています:

for i in range(10):
    print(i)

    if i == 9:
        print("Too big - I'm giving up!")
        break;
else:
    print("Completed successfully")

しかし、ここでelseがキーワードとして使用されている理由はわかりません。問題のコードは、forブロックが完了しない場合にのみ実行されることを示唆しているためです。どう考えても、私の頭脳はforステートメントからelseブロックまでシームレスに進むことはできません。私にとっては、continuecontinuewithがもっと理にかなっているでしょう(そして私は自分自身でそれを読むように訓練しようとしています)。

私は、Pythonのコーダーがどのようにしてこの構文を頭の中で読んでいるのか疑問に思っています。おそらく、そのようなコードブロックをもっと簡単に解読できるようなものが足りないのでしょうか。

371
Kent Boogaart

それはベテランのPythonコーダーにとってさえ奇妙な構造です。 forループと組み合わせて使用​​する場合、基本的には「イテラブル内の項目を検索します。それ以外の項目が見つからない場合は...」を意味します。のように:

found_obj = None
for obj in objects:
    if obj.key == search_key:
        found_obj = obj
        break
else:
    print('No object found.')

しかし、あなたがこの構造を見たときはいつでも、より良い代替案は検索を関数にカプセル化することです:

def find_obj(search_key):
    for obj in objects:
        if obj.key == search_key:
            return obj

あるいはリスト内包表記を使う:

matching_objs = [o for o in objects if o.key == search_key]
if matching_objs:
    print('Found {}'.format(matching_objs[0]))
else:
    print('No object found.')

意味的には他の2つのバージョンと同等ではありませんが、リスト全体を反復してもしなくても問題のない、パフォーマンスが重要でないコードでは十分に機能します。そうでない人もいるかもしれませんが、私は個人的に本番コードでfor-elseまたはwhile-elseブロックを使用することは避けたいと思います。

もご覧ください。[Python-ideas] for ... elseスレッドのまとめ

222

一般的な構成は、何かが見つかるまでループを実行してからループから抜け出すことです。問題は、ループから抜け出した場合、またはループが終了した場合、どちらのケースが発生したのかを判断する必要があることです。 1つの方法は、ループがどのように終了したかを確認するために2回目のテストを実行できるようにするフラグまたはストア変数を作成することです。

たとえば、リストを検索して、フラグ項目が見つかるまで各項目を処理してから処理を停止する必要があるとします。フラグ項目がない場合は、例外を発生させる必要があります。

あなたが持っているPythonのfor...else構文を使って

for i in mylist:
    if i == theflag:
        break
    process(i)
else:
    raise ValueError("List argument missing terminal flag.")

これを、この構文糖を使用しない方法と比較してください。

flagfound = False
for i in mylist:
    if i == theflag:
        flagfound = True
        break
    process(i)

if not flagfound:
    raise ValueError("List argument missing terminal flag.")

最初のケースでは、raiseはそれが動作するforループにしっかりと束縛されています。 2番目の場合、バインディングはそれほど強くなく、メンテナンス中にエラーが発生する可能性があります。

449
Lance Helsten

Transforming Code to Beautiful、Idiomatic Python というタイトルのレイモンド・ヘッティンガーによる素晴らしいプレゼンテーションがあります。 for ... elseコンストラクト。関連するセクションは、「ループ内の複数の出口点の識別」です。 15:50から開始 で、約3分間続きます。高い点は次のとおりです。

  • for ... elseコンストラクトは、特定のGOTOユースケースの代替としてDonald Knuthによって考案されました。
  • elseキーワードの再利用は、「Knuthが使用したものであり、当時、すべての[forステートメント]がifGOTOを埋め込み、彼らはelse;を期待していました」
  • 後知恵では、「no break」(または「nobreak」)と呼ばれるべきでしたが、混乱することはありませんでした。*

そのため、「このキーワードを変更しないのはなぜですか」という質問であれば、それから Cat Plus Plusがおそらく最も正確な答えを与えた –この時点では、既存のコードを破壊しすぎて実用的ではないだろう。しかし、あなたが本当に尋ねている質問がelseがそもそも再利用された理由であるなら、それは明らかに、当時は良いアイデアのように思えた。

個人的には、elseが一目でループ内にあると誤解される可能性のある場所で# no breakをインラインでコメントする妥協が好きです。合理的かつ簡潔です。このオプションは、答えの最後に Bjornがリンクした要約 で簡単に言及しています。

完全を期すために、構文を少し変更するだけで、この構文が必要なプログラマーがすぐに使用できるようになります。

for item in sequence:
    process(item)
else:  # no break
    suite

*ビデオのその部分からのボーナス引用:「ちょうどlambdamakefunction、を呼び出したかのように、「ラムダは何をするのか?」

145
Air

彼らはその言語に新しいキーワードを紹介したくなかったからです。それぞれが識別子を盗み、後方互換性の問題を引き起こすので、それは通常最後の手段です。

31
Cat Plus Plus

それを簡単にするために、あなたはそれをそのように考えることができます。

  • breakループ内でforコマンドが検出された場合、else部分は呼び出されません。
  • breakループ内でforコマンドが検出されない場合は、else部分が呼び出されます。

言い換えれば、forループの繰り返しがbreakで "壊れていない"場合、else部分が呼び出されます。

18
Ad Infinitum

For/elseが何をしたのか、そしてもっと重要なのはいつ使うのかを知るための最も簡単な方法はbreak文がどこにジャンプするかに集中することでした。 For/else構文は単一ブロックです。 breakはブロックから飛び出して、else節を飛び越えます。 else節の内容が単にfor節の後に続いた場合、それは飛び越されることは決してないだろうから、それをifに入れることによって同等のロジックを提供しなければならないだろう。これは以前にも言われていましたが、これらの言葉では全くそうではなかったので、それは他の誰かを助けるかもしれません。次のコードを実行してみてください。明快さのために 'ノーブレーク'コメントに賛成です。

for a in range(3):
    print(a)
    if a==4: # change value to force break or not
        break
else: #no break  +10 for whoever thought of this decoration
    print('for completed OK')

print('statement after for loop')
15
Neil_UK

ドキュメンテーションにはelsecontinueの素晴らしい説明があると思います

[...]リストが使い果たされてループが終了したとき(with)または条件がfalseになったとき(while)に実行されますが、ループがbreakステートメントで終了したときには実行されません。

出典: Python 2 docs:制御フローに関するチュートリアル

14
Ayan

私はそれを次のように読みました:

それでもループを実行する条件にある場合は、else何かを実行します。

12
pcalcao

技術的な部分はほとんど答えられているので、私のコメントはこれを生み出す混乱との関係にすぎませんrecycledキーワード。

Pythonは非常に雄弁なプログラミング言語であるため、キーワードの誤用はより悪名高いです。 elseキーワードは決定木の流れの一部を完全に記述したものです。私たち自身の言語では暗黙のです。

代わりに、このキーワードをwhileおよびforステートメントと共に使用すると混乱が生じます。その理由は、プログラマーとしての私たちのキャリアから、elseステートメントは決定木の中にあることを私たちに教えてくれました。その論理的範囲条件付きでに続くパスを返すラッパー。一方、ループ文は何かを達成するための比喩的な明確な目標を持っています。目標はプロセスの継続的な反復の後に達成されます。

if / elseは、に続くパスを示します。は、「ゴール」が完了するまでパスをたどります

問題は、elseが条件の最後のオプションを明確に定義するWordであるということです。 Wordのセマンティクスは、PythonとHuman Languageでは、両方ともsharedです。しかし、それ以外の人の言葉は、何かが完成した後に誰かまたは何かがとる行動を示すのに使われることは決してありません。完了の過程で問題が発生した場合に使用されます(break文のようなものです)。

最後に、キーワードはPythonに残ります。すべてのプログラマーがニーモニックデバイスのようにその使い方を理解するためのストーリーを考え出そうとするとき、それが間違いであることは明らかです。彼らが代わりにキーワードthenを選んだなら、私は大好きでした。私はこのキーワードがその繰り返しの流れ、ループの後のpayoffに完全に適合すると信じています。

それはおもちゃを組み立てることにおけるあらゆるステップをたどった後に何人かの子供が持っているその状況に似ています:そしてそして何お父さん?

7
3rdWorldCitizen

iterableが完全に使い果たされ、実行がforを終えた後に次のステートメントに進む直前に、else節が実行されるように」と読みました。したがって、反復がbreakによって中断されると、これは実行されません。

5
0xc0de

私は同意します、それは「Elifが[条件が中断を起こす]ではない]のようなものです

私はこれが古いスレッドであることを知っています、しかし私は今同じ質問を検討しています、そして私がそれを理解する方法で誰かがこの質問への答えを捕らえたと確信していません。

私にとっては、For... elseまたはWhile... elseステートメント内のelseを「読み取る」には3つの方法があります。これらはすべて等価です。

  1. else==if the loop completes normally (without a break or error)
  2. else==if the loop does not encounter a break
  3. else==else not (condition raising break)(おそらくそのような状態がある、あるいはあなたはループを持たないでしょう)

したがって、本質的に、ループ内の「else」は実際には「Elif ...」です。ここで、「...」は(1)改行なし、つまり(2)NOTと同じです。 [休憩が発生している条件]

私は、elseは 'break'なしでは無意味なので、for...elseには次のものが含まれることが重要だと思います。

for:
    do stuff
    conditional break # implied by else
else not break:
    do more stuff

そのため、for...elseループの基本的な要素は次のようになります。それらをわかりやすい英語で読むとします。

for:
    do stuff
    condition:
        break
else: # read as "else not break" or "else not condition"
    do more stuff

他のポスターが言っていたように、ループが探しているものを見つけることができるとき、一般的にブレークが発生します、それでelse:は「ターゲットアイテムが見つからなかったらどうするか」になります。

例外処理、ブレーク、およびforループをまとめて使用することもできます。

for x in range(0,3):
    print("x: {}".format(x))
    if x == 2:
        try:
            raise AssertionError("ASSERTION ERROR: x is {}".format(x))
        except:
            print(AssertionError("ASSERTION ERROR: x is {}".format(x)))
            break
else:
    print("X loop complete without error")

結果

x: 0
x: 1
x: 2
ASSERTION ERROR: x is 2
----------
# loop not completed (hit break), so else didn't run

打撃を受けている簡単な例。

for y in range(0,3):
    print("y: {}".format(y))
    if y == 2: # will be executed
        print("BREAK: y is {}\n----------".format(y))
        break
else: # not executed because break is hit
    print("y_loop completed without break----------\n")

結果

y: 0
y: 1
y: 2
BREAK: y is 2
----------
# loop not completed (hit break), so else didn't run

中断がない、中断を引き起こす条件がない、エラーが発生していない単純な例です。

for z in range(0,3):
     print("z: {}".format(z))
     if z == 4: # will not be executed
         print("BREAK: z is {}\n".format(y))
         break
     if z == 4: # will not be executed
         raise AssertionError("ASSERTION ERROR: x is {}".format(x))
else:
     print("z_loop complete without break or error\n----------\n")

結果

z: 0
z: 1
z: 2
z_loop complete without break or error
----------
4
NotAnAmbiTurner

elseキーワードはここでは混乱を招く可能性があります。多くの人が指摘しているように、nobreaknotbreakのようなものがより適切です。

for ... else ...を論理的に理解するために、try...except...elseではなくif...else...と比較してください。ほとんどのpythonプログラマーは以下のコードに精通しています:

try:
    do_something()
except:
    print("Error happened.") # The try block threw an exception
else:
    print("Everything is find.") # The try block does things just find.

同様に、breakを特別な種類のExceptionと考えてください。

for x in iterable:
    do_something(x)
except break:
    pass # Implied by Python's loop semantics
else:
    print('no break encountered')  # No break statement was encountered

違いはpythonexcept breakを意味しているので書き出すことができないので、次のようになります。

for x in iterable:
    do_something(x)
else:
    print('no break encountered')  # No break statement was encountered

はい、この比較は困難で面倒な場合があることを私は知っていますが、それは混乱を明確にしています。

4
cizixs

elseステートメントブロック内のコードは、forループが壊れていないときに実行されます。

for x in xrange(1,5):
    if x == 5:
        print 'find 5'
        break
else:
    print 'can not find 5!'
#can not find 5!

のドキュメントから:ステートメントを破って続行し、そうでなければループに関する節

ループ文はelse節を持つことができます。リストが使い果たされてループが終了したとき(with for)、または条件がfalseになったとき(with while)に実行されますが、ループがbreakステートメントで終了したときには実行されません。これは、素数を検索する次のループで例示されています。

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             break
...     else:
...         # loop fell through without finding a factor
...         print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3

(はい、これは正しいコードです。よく見てください。else節はif文ではなくforループに属しています。)

ループと組み合わせて使用​​する場合、else文は、if文のelse文よりも、try文のelse文とより共通しています。 。 try文と例外の詳細については、例外処理を参照してください。

同様にCから借りたcontinueステートメントは、ループの次の繰り返しに続きます。

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print("Found an even number", num)
...         continue
...     print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
3
GoingMyWay

elseは他のもののように、あるいは他のもののように考えることができます。それはループでは行われませんでした。

2
jamylak

これについて考える方法は、他の誰かが上で述べたことを見たことがないということです。

まず、forループは基本的にwhileループを囲む単なる構文上の糖であることを忘れないでください。例えば、ループ

for item in sequence:
    do_something(item)

次のように書き換えることができます。

item = None
while sequence.hasnext():
    item = sequence.next()
    do_something(item)

次に、whileループは基本的に単にifブロックを繰り返すだけであることを忘れないでください。 whileループを「この条件が真であれば本文を実行してから戻ってきてもう一度確認する」と読むことができます。

そのため、while/elseは完全に理にかなっています。条件を1回だけチェックするのではなく、条件がfalseになるまでループするという機能が追加された、i​​f/elseとまったく同じ構造です。

すべてのforループはwhileループの上にある単なる構文上の糖であるため、基礎となるwhileループの暗黙の条件が何であるかを理解する必要があります。条件はFalseになります。

2
Aaron Gable

これは検索以外の慣用句です。条件が真になるのを待っていたとしましょう。リモートサーバー上で開くためのポートとタイムアウトを設定します。そうすれば、while...else構文を次のように利用できます。

import socket
import time

sock = socket.socket()
timeout = time.time() + 15
while time.time() < timeout:
    if sock.connect_ex(('127.0.0.1', 80)) is 0:
        print('Port is open now!')
        break
    print('Still waiting...')
else:
    raise TimeoutError()
1
for i in range(3):
    print(i)

    if i == 2:
        print("Too big - I'm giving up!")
        break;
else:
    print("Completed successfully")

ここでの「その他」は非常に単純です。

1、「for clauseが完了したら」

for i in range(3):
    print(i)

    if i == 2:
        print("Too big - I'm giving up!")
        break;
if "for clause is completed":
    print("Completed successfully")

「for句は完成しました」というような長い文を書くことを望んでいるので、「else」を導入します。

ここでのelseは本質的にifです。

2、しかしfor clause is not run at allはどうですか

In [331]: for i in range(0):
     ...:     print(i)
     ...: 
     ...:     if i == 9:
     ...:         print("Too big - I'm giving up!")
     ...:         break
     ...: else:
     ...:     print("Completed successfully")
     ...:     
Completed successfully

つまり、それは完全に論理的な組み合わせです。

if "for clause is completed" or "not run at all":
     do else stuff

またはこのようにそれを置く:

if "for clause is not partially run":
    do else stuff

またはこのように:

if "for clause not encounter a break":
    do else stuff
1
DummyHead

素晴らしい答えは:

  • 歴史を説明する this 、そして
  • this はあなたの翻訳/理解を容易にする正しい引用を与えます。

ここでの注意点は、while-elseがif-elseと区別がつかない構造がある(Pythonで)というDonald Knuthが以前に言ったこと(申し訳ありませんが、参照が見つかりません)から来ました。

x = 2
while x > 3:
    print("foo")
    break
else:
    print("boo")

以下と同じフローを持ちます(低レベルの違いを除く)。

x = 2
if x > 3:
    print("foo")
else:
    print("boo")

要点は、if-elseはwhile-elseの構文上の糖と見なすことができ、breakブロックの最後に暗黙のifがあることです。 whileifの前に教えることが多いので、ifループはwhileの拡張であるという逆の意味がより一般的です(これは単なる繰り返し/ループ条件付きチェックです)。ただし、while-else内のelseブロックは 毎回 conditionがfalseの場合に実行されます。

あなたの理解を容易にするためにそのように考えてください。

breakreturnなどがないと、conditionが真でなくなった場合にのみループが終了し、そのような場合はelseブロックも1回実行されます。 Pythonのforの場合は、Cスタイルのforループ(条件付き)を検討するか、それらをwhileに変換する必要があります。

もう一つの注意:

ループ内の時期尚早のbreakreturnなどは、conditionがtrueの間に実行がループから飛び出して再びチェックするために戻ってこないため、conditionがfalseになることを不可能にします。

1
WloHu

Pythonはforループとwhileループの後にelseを使用しているため、ループに何も適用されない場合は何か他のことが起こります。例えば:

test = 3
while test == 4:
     print("Hello")
else:
     print("Hi")

出力は何度も何度も「こんにちは」になります(私が正しい場合)。

0
Mrmongoose64

関数があるとしましょう

def broken(x) : return False if x==5 else True

これは5つだけが壊れていないことを意味します。今の場合壊れたは決して5で評価されません: -

for x in range(4):
    if not broken(x) : break
else:
    print("Everything broken... Doom is upon us")

出力を与えます: -

Everything broken... Doom is upon us

Wherebrokenが5で評価される場合: -

for x in range(6):
    if not broken(x) : break
else:
    print("Everything broken... Doom is upon us")

何も印刷されません。したがって、間接的に言って壊れていないものが少なくともあるということです。

しかし、あなたが不正行為をしたいと思った場合、あなたが見つけたものが壊れているのをスキップしてください。つまり、5が壊れていることがわかってもループを続行します。それ以外の場合は、ステートメントは表示されたままになります。あれは :-

for x in range(6):
    if not broken(x) : continue
else:
    print("Everything broken... Doom is upon us")

印刷します

Everything broken... Doom is upon us

私はそれが新しいものを作成する代わりに混乱を解消することを願っています:-)

0

私はただ自分自身でそれを理解しようとしていました。私は次のことが役立つことがわかりました!

•(elseではなく)ループ内でifとペアになっているforを考えてください。そうでない場合はループを中断します。 1つのelseが複数のifとペアになっている場合を除き、これを実行してください。
ifsがまったく満足されない場合は、elsenameを実行します。
•複数のifsは、実際にはif-Elifsと見なすこともできます。

0
Germaine Goh