Pythonトライエルス


581

ステートメントのオプションのelse句の使用目的は何tryですか?


1
ほとんどの回答は、try句自体のelse句にマテリアルを配置できない理由に集中しているようです。stackoverflow.com/questions/3996329の質問では、else句のコードがtryブロック自体の後に配置できない理由を具体的に尋ねており、その質問はこの質問に当てはめられていますが、その質問に対する明確な回答はここにはありません。私はstackoverflow.com/a/3996378/1503120がその質問にうまく答えていると感じています。また、stackoverflow.com / a / 22579805/1503120でさまざまな句のさまざまな重要性を解明しようとしました。
jamadagni 14年

最後のクリーンアップの前に、例外がトリガーされない場合、それ自体が同じ例外処理をトリガーすることが想定されていない何かが発生する必要があります。
ベンジミン

回答:


858

elseブロック内のステートメントは、try例外が発生しなかった場合、実行が一番下に落ちた場合に実行されます。正直なところ、必要はありませんでした。

ただし、例外処理には次の点に注意してください。

else ...句を使用すると、try ...句にコードが追加されていないため、try ... exceptステートメントで保護されているコードで発生しなかった例外を誤ってキャッチすることがなくなります。

あなたは、例えば、投げることができ方法を持っているのであれば、IOError何か他のものを、そしてあなたはそれが提起例外をキャッチしたいのですが、そこのあなたは、最初の操作が成功した場合に何をしたい、とあなたがいないから例外IOErrorをキャッチしたいですその操作、あなたはこのようなものを書くかもしれません:

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
    # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

あなただけ置く場合はanother_operation_that_can_throw_ioerror()後にoperation_that_can_throw_ioerrorexcept2番目の呼び出しのエラーをキャッチします。そして、tryブロック全体の後に置くと、常に実行され、の後まで実行されませんfinallyelseあなたが確認してくださいすることができます

  1. 2番目の操作は、例外がない場合にのみ実行されます。
  2. finallyブロックの前に実行され、
  3. 発生したIOErrorsはここではキャッチされません

7
また、tryブロックで使用される変数はelseブロックでも使用できるため、elseブロックでこれ以上の例外が予想されない場合は、常にこのバリアントの使用を検討する必要があります
WorldSEnder

3
elseがあるかどうかにかかわらず、tryスコープの変数はtryの外に表示されるため、これは問題ではありません。
Reinderien 2014

36
「トライスコープ変数」のようなものはありません。Pythonでは、変数のスコープは、制御構造ではなく、モジュール、関数、および内包によってのみ確立されます。
mhsmith 2015

9
else句を使用すると、例外がスローされなかった場合にのみ意味のあるコードを記述できます。except節は単純に渡すことができます。ロジックをtryブロックに配置すると、コードのバグを黙って隠すリスクがあります。予期しない例外をつぶさないでください。
アリスパーセル

9
それは何この回答から明らかではない「ボトム落下し、」手段-これは例外のために起こるだけでなく、理由のないだけではなくreturncontinuebreak
Antti Haapala 2016

108

使用する大きな理由が1つありelseます。スタイルと読みやすさです。一般に、例外を引き起こす可能性のあるコードは、それらを処理するコードの近くに置くことをお勧めします。たとえば、これらを比較します。

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

そして

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

2つ目はexcept、早期に復帰できない場合、または例外を再スローできない場合に適しています。可能であれば、私は書いたでしょう:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

注:ここに最近投稿された複製から回答がコピーされたため、この「AskPassword」に関するものすべて。


53

1つの用途:例外を発生させるコードをテストします。

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(このコードは、実際にはより一般的なテストに抽象化する必要があります。)


50

Pythonトライエルス

elsetryステートメントのオプションの句の使用目的は何ですか?

意図された用途は、処理されるはずの例外がなかった場合に実行するコードを増やすためのコンテキストを持つことです。

このコンテキストにより、予期しないエラーを誤って処理することが回避されます。

ただし、、、およびがへの制御フローを中断する可能性があるためreturn、else句を実行させる正確な条件を理解することが重要です。continuebreakelse

要約すれば

else存在しない場合のステートメントが実行一切の例外とによって中断されていない場合returncontinueまたはbreak声明。

他の答えはその最後の部分を逃しています。

ドキュメントから:

オプションのelse句は、制御が句の終わりから流れ出した場合に実行されtryます。*

(太字が追加されました。)そして脚注には次のように書かれています。

*現在、制御が例外またはの実行の場合を除き、「末尾から流れ」returncontinueまたはbreak声明。

少なくとも1つの先行するexcept節が必要です(文法を参照)。したがって、これは実際には「try-else」ではなく、「try-except-else(-finally)」であり、else(およびfinally)はオプションです。

Pythonのチュートリアルの使用目的について詳しく説明:

try ... exceptステートメントにはオプションのelse節があり、存在する場合は、すべてのexcept節の後に続く必要があります。これは、try句が例外を発生させない場合に実行する必要があるコードに役立ちます。例えば:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

else ...句を使用すると、try ...句にコードが追加されていないため、try ... exceptステートメントで保護されているコードで発生しなかった例外を誤ってキャッチすることがなくなります。

ブロックのelse後にコードを区別する例try

エラーを処理すると、elseブロックは実行されません。例えば:

def handle_error():
    try:
        raise RuntimeError('oops!')
    except RuntimeError as error:
        print('handled a RuntimeError, no big deal.')
    else:
        print('if this prints, we had no error!') # won't print!
    print('And now we have left the try block!')  # will print!

そしていま、

>>> handle_error()
handled a RuntimeError, no big deal.
And now we have left the try block!

26

try-except-elseは、EAFPパターンダックタイピングを組み合わせるのに最適です。

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

この素朴なコードで問題ないかもしれません:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

これは、コード内の重大なバグを誤って非表示にする優れた方法です。そこでクリーンアップを打ち消しましたが、知らせてくれるAttributeErrorが飲み込まれています。さらに悪いことに、正しく記述したが、クリーンアップメソッドに誤った名前の属性を持つユーザータイプが渡されたため、途中で警告なしに失敗し、ファイルが閉じられていない場合はどうなりますか?これをデバッグして頑張ってください。


19

例外があっても実行する必要があるクリーンアップがある場合、私はそれが本当に便利だと思います。

try:
    data = something_that_can_go_wrong()
except Exception as e: # yes, I know that's a bad way to do it...
    handle_exception(e)
else:
    do_stuff(data)
finally:
    clean_up()

9

現時点ではその使用法を考えることはできませんが、それを使用する必要があるに違いありません。これは想像力に欠けるサンプルです:

else

a = [1,2,3]
try:
    something = a[2]
except:
    print "out of bounds"
else:
    print something

なしelse

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

ここでは、somethingエラーがスローされない場合に変数が定義されています。これをtryブロックの外で削除することはできますが、変数が定義されている場合は乱雑な検出が必要になります。


3
something = a[2]; print somethingtry:ブロック内の何が問題になっていますか?
S.Lott、2009年

@ S.Lott何もありませんが、誰かがあなたにリストを送信していて、破損している可能性があるため、データが十分に長くない場合にデータを表示したくない場合はどうでしょうか。
不明

12
S.ロット:「何かを印刷」すると、傍受したくない別の例外が発生する可能性があります。
ダライアスベーコン

違いはわかりません。範囲外の例外が発生すると、「範囲外」と出力されます。わかった。他の例外が発生した場合、このコードブロックでは捕捉できません。例外が発生しない場合、動作は何かの値を出力することです。これはa [2]です。この例では、elseが何をするのかわかりません。
S.Lott、2009年

3
'something'の値が出力されると、その__str __()メソッドでエラーが発生する可能性があります。この例では実際にはその値は2にすぎませんが、ここでも範囲外の例外はないことも指摘しておく必要があります。
ダライアスベーコン

8

良い例がありますtry-elseではPEP 380で。基本的には、アルゴリズムのさまざまな部分でさまざまな例外処理を実行することになります。

それは次のようなものです:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

これにより、例外が発生する場所により近い場所で例外処理コードを記述できます。


7

エラーと例外から#例外の処理-docs.python.org

try ... except文は、オプションの持っているelse現在、句以外のすべてに従わなければならない、句を、。これは、try句が例外を発生させない場合に実行する必要があるコードに役立ちます。例えば:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'cannot open', arg
    else:
        print arg, 'has', len(f.readlines()), 'lines'
        f.close()

else ...句を使用すると、try ...句にコードが追加されていないため、try ... exceptステートメントで保護されているコードで発生しなかった例外を誤ってキャッチすることがなくなります。


6

Pythonのリファレンスを見ると、例外がないときにelse実行されているようですtry。オプションのelse句は、制御がtry句の終わりから流れ出した場合に実行されます。2 else句の例外は、先行するexcept句では処理されません。

Pythonに飛び込むには、私が正しく理解している場合、tryブロックでモジュールをインポートしようとする例があります。それが失敗すると、例外が発生してデフォルトがバインドされますが、機能する場合は、elseブロックに移動して必要なものをバインドするオプションがあります(例と説明のリンク)。

catchブロックで作業しようとすると、別の例外がスローされる可能性があります-ここでelseブロックが便利になると思います。


4
「else句の例外は、前述のexcept句では処理されません。」それは便利な部分です。ありがとうございました。
geowa4 2009年

「オプションのelse節は、制御がtry節の終わりからフローする場合に実行されます」とは、tryブロックの外に戻ることができるため、もう1つの違いです。
Tomer W 2018

4

それでおしまい。try-except句の「else」ブロックは、試行された操作が成功したとき(およびそのときのみ)に実行されるコードに存在します。使用することも、悪用することもできます。

try:
    fp= open("configuration_file", "rb")
except EnvironmentError:
    confdata= '' # it's ok if the file can't be opened
else:
    confdata= fp.read()
    fp.close()

# your code continues here
# working with (possibly empty) confdata

個人的には気に入っており、必要に応じて利用しています。意味的にステートメントをグループ化します。


2

おそらく用途は次のとおりです。

#debug = []

def debuglog(text, obj=None):
    " Simple little logger. "
    try:
        debug   # does global exist?
    except NameError:
        pass    # if not, don't even bother displaying
    except:
        print('Unknown cause. Debug debuglog().')
    else:
        # debug does exist.
        # Now test if you want to log this debug message
        # from caller "obj"
        try:
            if obj in debug:
                print(text)     # stdout
        except TypeError:
            print('The global "debug" flag should be an iterable.')
        except:
            print('Unknown cause. Debug debuglog().')

def myfunc():
    debuglog('Made it to myfunc()', myfunc)

debug = [myfunc,]
myfunc()

たぶん、これはあなたにもあなたを導くでしょう。


2

try: ... else:データベースクエリを実行し、それらのクエリの結果を同じフレーバー/タイプの別のデータベースに記録している状況で、この構造が有用であることがわかりました。キューに送信されたデータベースクエリを処理する多くのワーカースレッドがあるとします

#in a long running loop
try:
    query = queue.get()
    conn = connect_to_db(<main db>)
    curs = conn.cursor()
    try:
        curs.execute("<some query on user input that may fail even if sanitized">)
    except DBError:
        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of failed query")
        logcurs.close()
        logconn.close()
    else:

        #we can't put this in main try block because an error connecting
        #to the logging DB would be indistinguishable from an error in 
        #the mainquery 

        #We can't put this after the whole try: except: finally: block
        #because then we don't know if the query was successful or not

        logconn = connect_to_db(<logging db>)
        logcurs = logconn.cursor()
        logcurs.execute("<update in DB log with record of successful query")
        logcurs.close()
        logconn.close()
        #do something in response to successful query
except DBError:
    #This DBError is because of a problem with the logging database, but 
    #we can't let that crash the whole thread over what might be a
    #temporary network glitch
finally:
    curs.close()
    conn.close()
    #other cleanup if necessary like telling the queue the task is finished

もちろん、スローされる可能性のある例外を区別できる場合、これを使用する必要はありませんが、コードの成功部分に反応するコードが成功部分と同じ例外をスローする可能性がある場合、 2番目の可能な例外を解除するか、成功するとすぐに戻ります(私の場合はスレッドが強制終了されます)。これは便利です。


1

elseブロックは、多くの場合、すべての中で発生した機能を補完するために存在することができますexceptブロックを。

try:
    test_consistency(valuable_data)
except Except1:
    inconsistency_type = 1
except Except2:
    inconsistency_type = 2
except:
    # Something else is wrong
    raise
else:
    inconsistency_type = 0

"""
Process each individual inconsistency down here instead of
inside the except blocks. Use 0 to mean no inconsistency.
"""

この場合、inconsistency_typeはブロック以外の各ブロックで設定されるため、のエラーのない場合の動作が補完されelseます。

もちろん、私はこれをいつかあなた自身のコードで現れるかもしれないパターンとして説明しています。この特定のケースでは、とにかくブロックのinconsistency_type前に0に設定しtryます。


1

これは、このパターンを使用する別の場所です。

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

1
continue代わりに「ブレイクアウトアーリー」パターンを使用できます。これにより、「else」句とそのインデントを削除して、コードを読みやすくすることができます。
Malthe

1

私が考えることができる使用シナリオの1つは、予測できない例外であり、再試行すると回避できます。たとえば、tryブロックの操作に乱数が含まれる場合:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

ただし、例外を予測できる場合は、例外よりも常に事前に検証を選択する必要があります。ただし、すべてを予測できるわけではないので、このコードパターンはその場所にあります。


1
あなたはこれをbreak内部に置くことtryで最後に行うことができます、それはよりきれいなIMOです、そしてあなたは必要としませんelse。また、これcontinueは実際には必要ありませんpass
Dirbaio 2017年

1

私を発見したelse可能性が間違った設定ファイルを扱うための便利:

try:
    value, unit = cfg['lock'].split()
except ValueError:
    msg = 'lock monitoring config must consist of two words separated by white space'
    self.log('warn', msg)
else:
     # get on with lock monitoring if config is ok

lock設定を読み取る例外はロックの監視を無効にし、ValueErrorsは有用な警告メッセージをログに記録します。


1

プログラミングロジックが、辞書に特定のキーを持つエントリがあるかどうかに依存するとします。コンストラクトをdict.get(key)使用した結果をテストするif... else...か、次のように実行できます。

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

-1

私は、DBセッションを処理するときに簡単に思える別の使用例を追加します。

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()

-17

else:ブロックは、混乱と(ほぼ)役に立ちません。これはforand whileステートメントの一部でもあります。

実際、ifステートメントでさえ、else:見つけるのが非常に難しいバグを作成する本当にひどい方法で悪用される可能性があります。

このことを考慮。

   if a < 10:
       # condition stated explicitly
   elif a > 10 and b < 10:
       # condition confusing but at least explicit
   else:
       # Exactly what is true here?
       # Can be hard to reason out what condition is true

についてよく考えてくださいelse:。一般的に問題です。- ifステートメント以外は避け、else-条件を文書化して明示的にすることを検討してください。


6
私はこれに同意しません。"if-elif"ブロックでは、 "else"がC言語の "case"ブロックで使用される "default"として使用されます。さまざまな状況ですべてのケースをカバーしたと考えている場合でも、常に「デフォルト」のケースを処理することをお勧めします。
Josip

1
@Josip:「デフォルト」として使用すると、混乱する可能性があります。問題は、この「デフォルト」である条件を明確に定義することです。不適切に定義されたデフォルト条件は、バグのある動作の根本的な原因となる可能性があります。それ以外の場合は混乱の原因となる可能性があります。すべての場合において、試行するだけでなく、しばらくの間でも、十分に注意深く検討する必要があります。
S.Lott、2009年

5
さて、上記のコードは完全に抽象的なものであり、意味のあることは何もしないので、そうです-紛らわしいのも不思議ではありません。
julx

1
@ S.Lott「それはバグを減らすだろう」-そして私のポイントはこれが間違っているということです。私たちは意見の本当の違いがあると思います。悪いプログラマは常にバグのあるプログラムを書く方法を見つけます。常に。優れたプログラマーは常に優れたプラクティスを模索しており、あらゆる言語で優れたコードを記述できます。有用なコンストラクトを削除しても、優れたプログラマーに与える電力は少なくなりますが、特に問題のあるコンストラクトを無数に作り出すことができるため、悪いコンストラクトを助けることはできません。
julx

5
検討してください:if x > 0: return "yes"if x <= 0: return "no"。今、人が来て、言うべき条件の1つを変更しますx > 1が、他の条件を変更するのを忘れます。コミットされるバグの数を減らすことはどうですか。if else句は、多くの場合、行が離れています。DRYは良い習慣です。(二重ポストのため申し訳ありません)。
julx
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.