Pythonの例外処理ブロックでreturnを行ったときの挙動
検証内容
Python3において、try/except/else/finally文中でreturnを記述した時の挙動と、例外を入れ子にした時の挙動をprintとreturnを使って検証する。
printが表示されれば、どのブロックで処理が実行されるかとその順番がわかる。
returnは関数中で1度しか実行されないので、どのブロックで優先的にreturnが起きるかがわかる。
例外無し
まずは、普通の例外処理の動きを見てみる。
try/except/else/finallyブロック内でreturnを行わず、例外が発生しなければ
-
tryブロックでprint実行
-
elseブロックでprint実行
-
finallyブロックでprint実行
-
例外処理ブロックの外でprint実行
-
例外処理ブロックの外でreturn
というように、exceptブロックには入らず、上から順番に実行されていく。
def non_raise_except():
try:
print("print in try-block")
except:
print("print in except-block")
else:
print("print in else-block")
finally:
print("print in finally-block")
print("print outside try-except-else-finally-block")
return "return outside try-except-else-finally-block"
print in try-block
print in else-block
print in finally-block
print outside try-except-else-finally-block
return outside try-except-else-finally-block
try-blockで例外発生
try/except/else/finallyブロック内でreturnを行わず、tryブロック内で例外が発生すると
-
tryブロックでprint実行
-
exceptブロックでprint実行
-
finallyブロックでprint実行
-
例外処理ブロックの外でprint実行
-
例外処理ブロックの外でreturn
というように、elseブロックには入らず、上から順番に実行されていく。
def raise_except_in_try():
try:
print("print in try-block")
raise Exception("Exception in try-block")
except:
print("print in except-block")
else:
print("print in else-block")
finally:
print("print in finally-block")
print("print outside try-except-else-finally-block")
return "return outside try-except-else-finally-block"
print in try-block
print in except-block
print in finally-block
print outside try-except-else-finally-block
return outside try-except-else-finally-block
try/except/else-block内でreturn
try/except/elseブロック内とその外側でreturnを記述した場合、例外が発生しなければ
-
tryブロックでprint実行
-
finallyブロックでprint実行
-
tryブロックでreturn
というように、tryブロックでreturnされる前にfinallyブロックの処理が実行される。
def return_in_try():
try:
print("print in try-block")
return "return in try-block"
except:
print("print in except-block")
return "return in except-block"
else:
print("print in else-block")
return "return in else-block"
finally:
print("print in finally-block")
print("print outside try-except-else-finally-block")
return "return outside try-except-else-finally-block"
print in try-block
print in finally-block
return in try-block
finally-block内でreturn
try/except/else/finallyブロック内とその外側でreturnを記述した場合、例外が発生しなければ
-
tryブロックでprint実行
-
finallyブロックでprint実行
-
finallyブロックでreturn実行
というように、returnされる前にfinallyブロックが実行され、returnはfinallyブロックから返される。
def return_in_finally():
try:
print("print in try-block")
return "return in try-block"
except:
print("print in except-block")
return "return in except-block"
else:
print("print in else-block")
return "return in else-block"
finally:
print("print in finally-block")
return "return in finally-block"
print("print outside try-except-else-finally-block")
return "return outside try-except-else-finally-block"
print in try-block
print in finally-block
return in finally-block
ネストした例外処理のfinally-block内でreturn
例外処理をネストしてそれぞれのtry/except/else/finallyブロック内でreturnを記述した場合、
-
親tryブロックでprint実行
-
子tryブロックでprint実行
-
子finallyブロックでprint実行
-
親finallyブロックでprint実行
-
親finallyブロックでreturn
というように、親と子のtryブロックの処理が実行された後、子のfinallyブロックが実行され、最終的に親のfinallyブロックからreturnされる。
def return_in_finally_nest():
try:
print("print in try-block")
try:
print("print in child-try-block")
return "return in child-try-block"
except:
print("print in child-except-block")
return "return in child-except-block"
else:
print("print in child-else-block")
return "return in child-else-block"
finally:
print("print in child-finally-block")
return "return in child-finally-block"
return "return in try-block"
except:
print("print in except-block")
return "return in except-block"
else:
print("print in parent-else-block")
return "return in parent-else-block"
finally:
print("print in parent-finally-block")
return "return in parent-finally-block"
print in try-block
print in child-try-block
print in child-finally-block
print in parent-finally-block
return in parent-finally-block
まとめ
-
returnはfinally内に書かれたものが最優先
-
finallyにreturnが無く、try/except/else内にreturnがある場合、return直前にfinallyブロックの処理が実行される
-
ネストされた例外処理でfinally内にreturnが書かれている場合、親のfinallyブロックのreturnが優先される
「finallyにreturnを記述してはいけない」と言われるのは、try/except/elseブロックのreturnが機能せず、ネストした場合には、すべて親のfinallyブロックのreturnに吸収されるため。
また、公式ドキュメントで、finallyブロックでreturnが記述されている場合には、try/except/elseブロック内でraiseされた例外が機能しないと説明されている。
Discussion