括弧なしのexcept/except*式を許可[3.14]
Python 3.14では、except/except*式の記述方法のルールが緩和されました。これらを使って複数の例外をキャッチする場合、従来は例外のリストを丸カッコで囲む必要がありました。
try: connect_to_server() except (TimeoutError, ConnectionRefusedError): print("ネットワーク接続に問題があります。")
Python 3.14では、こういった自明な記述には丸カッコは不要ということで、省略しても構わないとされました(従来通りに記述しても問題ありません)。
try: connect_to_server() except TimeoutError, ConnectionRefusedError: print("ネットワーク接続に問題があります。")
ただし、as句を使う場合には従来通り丸カッコが必要となります。
try: connect_to_server() except (TimeoutError, ConnectionRefusedError) as e: print("ネットワーク接続に問題があります。")
ここまではexcept式で説明してきましたが、ExceptionGroupを捕捉するexcept*式でも同様です。ExceptionGroupとexcept*式については第4回で紹介しています。
try: connect_to_server() except* TimeoutError, ConnectionRefusedError: print("ネットワーク接続に問題があります。")
アノテーションの遅延評価[3.14]
Python 3.14では、型アノテーションの評価タイミングが即時から利用時に変更され、例えば前方参照となるような型を使ったアノテーションでも問題なく使えるようになりました。
従来は、以下のリストのアノテーションは、実行時にNameErrorとなっていました。func関数の定義時点でクラスMyClassの定義が完了していないので、こうしたエラーになります。これは即時評価(Eagerly evaluation)と呼ばれます。
class MyClass: def func(arg: MyClass) -> MyClass: # 3.13ではNameError return arg
これを解決するには、次に説明する文字列アノテーションを使用する必要がありました。
文字列アノテーション
文字列アノテーション(Stringized annotations)とは、型を文字列リテラルとして表記する仕組みで、Python 3.7で導入されました。この仕組みを使うと、上のリスト中で型アノテーションとして使われているMyClassは"MyClass"と表記することも可能で、この場合はNameErrorとはなりません。
class MyClass: def func(arg: "MyClass") -> "MyClass": return arg
文字列アノテーションは、使用されるときになって実際の型情報を取得します(評価の延期(Postponed evaluation))。
ただし、いちいち文字列リテラルとしてアノテーションを記述するのもわずらわしいので、annotationsモジュールをimportすることでも、型名を自動で文字列化できます。
from __future__ import annotations # 追加 class MyClass: def func(arg: MyClass) -> MyClass: return arg
このように文字列アノテーションを使うと、型の定義場所にかかわらずアノテーションに指定できますが、実際に型情報を取得する際のコストは即時評価に比べれば大きなものになります。また、一部のライブラリとの相性もよくないとされ、根本的な解決方法が模索されてきました。
遅延評価
Python 3.14ではアノテーションの遅延評価(lazily evaluation)が導入され、即時評価のサンプル(eager_annotation.py)はエラーとならなくなりました。もちろん、アノテーションとしてきちんと機能しますし、annotationsモジュールも不要です。表面上はこれで終わりなのですが、アノテーションの処理方法の変化を確認してみましょう。
以下のリストは、遅延評価が有効なPython 3.14で動作するコードです。
class MyClass: def func(arg: MyClass) -> MyClass: return arg c = MyClass() # 以降の2行をstr_annotation.pyにも追記 print(c.func.__annotations__)
実行すると、以下のように出力されます。関数の__annotations__は、アノテーションの情報を辞書形式で返す属性です。
{'arg': <class '__main__.MyClass'>, 'return': <class '__main__.MyClass'>}
str_annotation.pyにも、最後の2つの文を追記して実行してください。実行結果が以下のように変わります。
{'arg': 'MyClass', 'return': 'MyClass'}
ポイントは、同じ辞書形式でも、値がクラス情報と文字列という違いがあることです。遅延評価ではアノテーション情報に型の情報が含まれるので、必要になればすぐに使用できます。文字列アノテーションでは、改めて型情報を取得するコストが必要でした。
なお、Python 3.14で導入されたannotationlibを使うと、get_annotations関数によりアノテーションの情報を複数の形式で取得できます。
from annotationlib import get_annotations, Format …略… print(get_annotations(c.func, format=Format.VALUE)) # 実行時の値として評価 print(get_annotations(c.func, format=Format.FORWARDREF)) # 未定義の名前を特別なマーカーに置き換え print(get_annotations(c.func, format=Format.STRING)) # アノテーションを文字列として返す
このように、遅延評価が導入されたことで、型の定義順でエラーが発生することを特別な技法を使わずに避けることができ、しかも実行時のコストも最小限にできるようになりました。なお、これに伴い文字列アノテーション化はPython 3.13のサポート終了とともに非推奨となり、最終的には廃止される見込みです。
まとめ
今回は、テンプレート文字列リテラルt-strings、高効率な圧縮のためのZstandard、括弧なしのexcept/except*式、アノテーションの遅延評価など、言語仕様やライブラリの強化を中心に紹介しました。
次回は、フリースレッドPythonの正式サポート、テイルコールに基づく新型インタプリタによる高速化、エラーメッセージの改善や構文強調表示など、インタプリタや言語インタフェースの強化を中心に紹介します。