Python 3.14新機能:より安全なコードを目指して。例外処理における2つの改善点Python最新情報キャッチアップ

Python 3.14では例外に関して2つの点が改善されています。1つのexcept節で複数の例外クラスを補足する場合に、それらの列挙が簡潔になったことと、finally節にreturn文などを置くとSyntaxWarningが発生するようになったことがそうです。これらについて見てみましょう。

» 2025年11月07日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「Python最新情報キャッチアップ」のインデックス

連載目次

 Python 3.14では例外に関連して2つの分布上の改善/安全性向上が行われている。

  • PEP 758:1つのexcept節(またはexcept*節)で複数の例外を補足するときに、かっこ「()」で囲むことなく、それらを並べられるようになった
  • PEP 765:finally節にreturn文/break文/continue文を記述するとSyntaxWarningが発生するようになった

 以下ではこれら2つについてまとめる。


かわさき

 どうもHPかわさきです。

 約1カ月続けてきた自称「Python祭り」ですが、今回の記事で取りあえずの終了となります。Python 3.14の新機能については本記事を含めて6本を公開しました。

 結構頑張ったつもりなんですが、どうなんでしょ。あまり人気企画にはならなかった印象なのですが、「新機能なんてどうでもいいよ」って方や、「ちょっとマニアックすぎて今回はパス」って方も結構いらっしゃるんですかねぇ。うーむ(遠い目)。でも、Pythonの言語仕様も毎年少しずつ変化していくので、新機能が出たときに軽く触れておく方が、無理なく知識を積み上げられて楽かなと思います。もしまだ知らない情報があれば、ぜひ他の記事もご笑覧ください。


PEP 758

 Python 3.13までは例外を補足するexcept節(または例外グループに含まれる例外のいずれかを補足する)で複数の種類の例外クラスを記述する場合はかっこ「()」内にそれらを並べる必要があった。以下はその例だ。

from random import randint

def raise_exception():
    exceptions = [TypeError(0), OSError(1), ValueError(2)]
    raise exceptions[randint(0, 2)]

def raise_and_catch():
    try:
        raise_exception()
    except (TypeError, ValueError):
        print('type error or value error')
    except OSError:
        print('OS error')

複数の例外を1つのexcept節で補足するには、それらをかっこで囲んで列挙する

 1つ目のexcept節ではタプルを使って「(TypeError, ValueError)」のように複数の例外クラスを列挙している点に注目しておこう。そして、Python 3.14では例外クラスの列挙の後に「as」がない場合には、複数の例外クラスを列挙する際にかっこで囲む必要がなくなった。

from random import randint

def raise_exception():
    exceptions = [TypeError(0), OSError(1), ValueError(2)]
    raise exceptions[randint(0, 2)]

def raise_and_catch():
    try:
        raise_exception()
    except TypeError, ValueError:
        print('type error or value error')
    except OSError:
        print('OS error')

raise_and_catch()

Python 3.14では複数の例外クラスを列挙する際にかっこが不要になった(一部制限あり)

 先ほど複数の例外クラスの指定にタプルを使っていると述べた。ここでタプルの性質を思い出してほしい。それは「タプルをタプルとしているのは、それを囲むかっこではなく、要素を列挙するためのカンマである」ということだ。簡単な例を示す。

t = (1,)
t = 1
x, y = 1, 2

かっこがなくてもタプルはタプル

 1行目の例は1要素のタプルを作成しているが、2行目のようにかっこを省略できる。むしろ、これがタプルであることを示すのはカンマである。3行目では代入文の左辺も右辺もタプルだが、かっこを省略して表記している。

 except節(except*節)でも同じように、タプルを囲むかっこを省略できるようになったというのがPEP 758の提案といえる。これはPEP 758でも「This change would bring the syntax more in line with other comma-separated lists in Python, such as function arguments, generator expressions inside of a function call, and tuple literals, where parentheses are optional.」と述べられている。適当に訳すと「この変更により、Pythonにおけるカンマ区切りの列挙構文との一貫性がより高まります。例えば、関数の引数、関数呼び出しに含めるジェネレータ式、タプルリテラルなど、かっこを省略できる場面がそうです」とでもなるだろう。

 かっこを省略できることには、わずかだがコードが読みやすくなるという効果もあるだろう。

 ただし、asを付ける場合にはこれまでと同様に、複数の例外クラスをかっこで囲む必要がある。

def raise_and_catch():
    try:
        raise_exception()
    except TypeError, ValueError as e:  # SyntaxError
        print('type error or value error')
    except OSError:
        print('OS error')

asを使うときには複数のクラスをかっこで囲まないとSyntaxError

 実際に試した結果を以下に示す。

asを使うときには複数のクラスをかっこで囲む必要がある asを使うときには複数のクラスをかっこで囲む必要がある

 このときには、以下に示すようにかっこを使う。

def raise_and_catch():
    try:
        raise_exception()
    except (TypeError, ValueError) as e:  # OK
        print('type error or value error')
    except OSError:
        print('OS error')

asを使う場合は複数の例外クラスをかっこで囲む

 なお、asの使用に関係なく、これまでのように複数の例外クラスをかっこで囲んでも何ら問題はない。以前のコードもこれまで通りにPython 3.14で動くはずだ。


かわさき

 パッと見では「細かいなぁ」と思える改善点ですが、タプルなどの表記に見られる省略してもよいところでは、かっこを省略して記述できるというルールとの一貫性を取るようにしたと考えると、なかなか趣深いですね(謎の上から目線)。


PEP 765

 PEP 765はfinally節でのreturn文/break文/continue文の使用を制限し、より安全なコードを書けるようにするものだ。Python 3.14ではSyntaxWarningが発生する。「SyntaxError例外が発生するようになるかどうか」「そうだとしていつからそうなるか」については未定だ。

 finally節にこれらの文を置くと、それまでに発生した例外が握りつぶされたり、try節から返されたはずの値が上書きされたりする可能性がある。これは混乱の素であり、バグを生み出す要因ともなる。そのために、Python 3.14ではそうしたコードについてはSyntaxWarningを発生させるようになった。

 簡単な例を見てみよう。

def foo():
    foo()

foo()

再帰呼び出しによってコールスタックが溢れて例外を発生させる

 foo関数は自身を再帰的に呼び出しているので、いつかはコールスタックがあふれて、例外が発生する。

RecursionError例外が発生したところ RecursionError例外が発生したところ

 では、try文を使ってこのコードを次のようにしてみよう。

def foo():
    try:
        foo()
    finally:
        return

foo()

finally節にreturn文を記述

 Python 3.13でこのコードを実行すると、次のようにRecursionError例外が握りつぶされてしまう。

Python 3.13では、foo関数は例外を発生させずに終了する Python 3.13では、foo関数は例外を発生させずに終了する


かわさき

 Python 3.14の構文ハイライトを見慣れると、Python 3.13のREPL(PyREPL)が色あせたものに見えてきますね(笑)。


 一方、Python 3.14では次のコードを実行してみると、foo関数の定義が終わった時点でSyntaxWarningが発生する。ただし、foo関数自体は例外を発生させることなく終了する。

Python 3.14では、foo関数を定義した時点でSyntaxWarningが発生する。ただし、foo関数自体は例外を発生させることなく終了する Python 3.14では、foo関数を定義した時点でSyntaxWarningが発生する。ただし、foo関数自体は例外を発生させることなく終了する

 このようにSyntaxWarningを発生させることでfinally節に危険なコードが含まれていることが分かるようになった。

 次のコードは、try節では0を返しているが、finally節では1を返している。

def foo():
    try:
        return 0
    finally:
        return 1

foo()

finally節がtry節で返している値を上書きしてしまう

 finally節は必ず実行されるので、try節で返そうとした値がfinally節にあるreturn文で上書きされてしまう。このような場合にもPython 3.14では関数定義の時点でSyntaxWarningを発生させるようになった。

 break文やcontinue文でも同様だ。

 そもそも、finally節にこれらの文を記述することに問題がある。finally節ではリソースの解放といった後片付けに相当する処理だけを記述することを心がけるべきだが、コードを書いていると、いつの間にかそういうコードが出来上がっていることもある。SyntaxWarningを発生させるようになったことで、そうした間違いに気付くタイミングが得られるようになり、プログラムのより安全な動作が期待できるようになる。


かわさき

 PEP 765に関連するサンプルコードを考えていると、「いやぁ、そうは書かないでしょ」というコードばかりが思い浮かんでいました。だって、for文の中にあるtry文のfinally節でbreakするコードなんて書きそうもないじゃないですか。と思っていても、いつかそういう場面に出会うことになるのかもしれません。そんなときのために転ばぬ先の杖としてSyntaxWarningがあるのはよいですね。


「Python最新情報キャッチアップ」のインデックス

Python最新情報キャッチアップ

Copyright© Digital Advantage Corp. All Rights Reserved.

アイティメディアからのお知らせ

スポンサーからのお知らせPR

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。