のにっき

【python】tryの中で呼び出した関数がエラーになった場合の挙動

pythonの組み込みで必ず使用するtryを使った例外処理の話です。

tryの中で呼び出した関数でエラー

tryの中で外部関数を呼び出した場合、
・外部関数でエラーになった時は呼び出し元のtryが動いてくれるのか?
・ちゃんとexcept以下の処理が動くのか?
・外部関数で外部関数を呼び出した時も同じ挙動になるのか?
・外部関数の中でtryが組み込まれていた場合はどうなるのか?

これらの挙動を簡単なソースで確認してみました。

【外部にtryなし】外部でエラーが起きた場合の挙動

A )外部の外部の外部でエラーが起きた場合

import sys
#******************************************************************************
# 呼び出す外部関数_1st
def SetDisp_01():
    print("01")
    SetDisp_02()
#******************************************************************************
# 呼び出す外部関数_2nd
def SetDisp_02():
    print('02')
    SetDisp_03()
#******************************************************************************
# 呼び出す外部関数_3rd
def SetDisp_03():
    print('03')
    print(1+"")# こいつがエラーになる行( int+str でエラー)   
#******************************************************************************
# メインルーチン
try:
    SetDisp_01()
    print("正常")
except:
    print("エラー")
    print(sys.exc_info())
    pass

このソースでは、メインルーチンのtryの中で
SetDisp_01を呼ぶ→SetDisp_02を呼ぶ→SetDisp_03を呼ぶ→エラー
という挙動になるように組み込みました。
実行結果

01
02
03
エラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12308>)

結果を見る限り、
外部でエラーになってもちゃんとtryが機能していて処理が中止されることはない。
ちゃんとエラー発生後にexcept以下の処理が実行されている
という事がわかりました。
B )外部でエラーが起きた場合、その後の処理は中断されるのか?

import sys
#******************************************************************************
# 呼び出す外部関数_1st
def SetDisp_01():
    print("01")
    print(1+"")# こいつがエラーになる行( int+str でエラー)
    SetDisp_02()
#******************************************************************************
# 呼び出す外部関数_2nd
def SetDisp_02():
    print('02')
#******************************************************************************
# メインルーチン
try:
    SetDisp_01()
    print("正常")
except:
    print("エラー")
    print(sys.exc_info())
    pass

このソースでは、メインルーチンのtryの中で
SetDisp_01を呼ぶ→エラー(の後、SetDisp_02を呼ぶ)
という挙動になるように組み込みました。
実行結果

01
エラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB126C8>)

結果を見る限り、
外部でエラーになってもエラー発生後にすぐ
except以下の処理が実行されているから、エラー後の処理は行われない。
という事がわかりました。

まとめ
・外部でエラーになってもちゃんとtryが機能していて処理が中止されることはない。
・ちゃんとエラー発生後にexcept以下の処理が実行されている
・外部でエラーになってもエラー発生後にすぐ
 except以下の処理が実行されているから、エラー後の処理は行われない。

【外部にtryあり】外部でエラーが起きた場合の挙動

A )外部のtryの中でエラーが起きた場合

import sys
#******************************************************************************
# 呼び出す外部関数_1st
def SetDisp_01():
    try:
        print("01")
        print(1+"")# こいつがエラーになる行( int+str でエラー)
    except:
        print("子01のエラー")
        print(sys.exc_info())
        pass
    SetDisp_02()
#******************************************************************************
# 呼び出す外部関数_2nd
def SetDisp_02():
    try:
        print("02")
        print(1+"")# こいつがエラーになる行( int+str でエラー)
    except:
        print("子02のエラー")
        print(sys.exc_info())
        pass    
#******************************************************************************
# メインルーチン
try:
    SetDisp_01()
    print("正常")
except:
    print("親のエラー")
    print(sys.exc_info())
    pass

このソースでは、メインルーチンのtryの中で
SetDisp_01(tryの中でエラー発生)→SetDisp_02(tryの中でエラー発生)
という挙動になるように組み込みました。
実行結果

0101のエラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12788>)
0202のエラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12B08>)
正常

B )外部のtryの外でエラーが起きた場合

import sys
#******************************************************************************
# 呼び出す外部関数_1st
def SetDisp_01():
    try:
        print("01")
        print(1+"")# こいつがエラーになる行( int+str でエラー)
    except:
        print("子のエラー")
        print(sys.exc_info())
        pass
    SetDisp_02()
#******************************************************************************
# 呼び出す外部関数_2nd
def SetDisp_02():
    print('02')
    print(1+"")# こいつがエラーになる行( int+str でエラー)
#******************************************************************************
# メインルーチン
try:
    SetDisp_01()
    print("正常")
except:
    print("親のエラー")
    print(sys.exc_info())
    pass

このソースでは、メインルーチンのtryの中で
SetDisp_01(tryの中でエラー発生)→SetDisp_02(tryの外でエラー発生)
という挙動になるように組み込みました。
実行結果

01
子のエラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12B48>)
02
親のエラー
(<class 'TypeError'>, TypeError("unsupported operand type(s) for +: 'int' and 'str'",), <traceback object at 0x000001DEFBB12B88>)

まとめ
A)とB)の結果を比較して、
・親tryの中で呼んだ外部関数のtry(以下、子try)の中でエラーが起きたら、
そのエラーは子tryの中で完結するので親tryから見たら正常扱いとなる。
・子tryはその中で完結しているので、子try外でのエラーは親tryのエラーとなる。
そのため、エラー発生時に処理は中断されて親tryのexcept以下の処理が実行される。

以上です。
組み込んでて色々気になることを検証しているのですが、
説明がめんどくさくて記事に出来ない事ばっかりなので難しいですね。
今回もこんなに記事が長くなるとは思いませんでした。。。まとめ力が成長しない。。。