SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Pythonの新機能を知ろう!

Python 3.13の新機能、対話型インタプリタの機能強化や高速化などを解説

Pythonの新機能を知ろう! 第5回

  • X ポスト
  • このエントリーをはてなブックマークに追加

CPythonの高速化[3.13]

 言語仕様ではありませんが、Pythonインタプリタの標準実装であるCPythonにさまざまな改良が施され、プログラムの実行がより高速になりました。これは、Python 3.11から始まった「Faster CPython project」に基づくものとされています。

 なお、Python 3.13で導入予定だったガベージコレクタの改良は、RC2でパフォーマンス低下などの症状が見られたためRC3でロールバックされ、Python 3.14以降の実装になりました。このガベージコレクタは、インクリメンタルサイクリックガベージコレクタと呼ばれ、メモリの解放を段階的に行い、プログラムの中断やパフォーマンスの低下を防ぐための仕組みです。

フリースレッドモード

 実験的実装ではありますが、フリースレッドモード(Free-threading mode)での実行がサポートされました。フリースレッドモードとは、同じインタプリタ内で複数のスレッドを並列実行できる機能です。マルチコアCPU環境でそのメリットを生かせるため、プログラムを高速に実行できます。

 フリースレッドモードのサポートは、グローバルインタプリタロック(GIL; Global Interpreter Lock)の廃止で実現されます。グローバルインタプリタロックとは、その名の通りインタプリタ全体で大域的に一つのロックを持つことです。シングルスレッドのプログラムではロック機構がシンプルなので、速度が向上する、C言語などのスレッドセーフでないライブラリの利用が容易になる、などのメリットを得られます。その反面、マルチスレッドのプログラムでは並行性に制限が加わり、思うような性能を得られないデメリットが発生します。

 Python 3.13では、このグローバルインタプリタロックをなくしたビルドを用いて、環境変数PYTHON_GILの設定かコマンドラインオプション-X gil=1を指定すると、フリースレッドモードで実行できます。グローバルインタプリタロックの廃止は破壊的変更とも位置付けられるので、完全に置き換えるのではなく、このように別のビルドを用意して徐々に移行する方針をとるようです。

JITコンパイラの実験的なサポート

 こちらも実験的ではありますが、JIT(Just-in-Time)コンパイラがサポートされました。JITコンパイラは、スクリプト言語の速度面の弱点を克服する手段として非常に有効ですが、意外なことにPythonはこれまでJITコンパイラをサポートしていませんでした。JavaScriptはJITコンパイラをいち早く取り入れ、Rubyも2016年でJITコンパイラをMJITとしてサポート、2022年にはYJITを搭載しています。Pythonも、この流れに乗ってより高速化が進められています。

 Python 3.13で取り入れられたJITコンパイラは、コピー&パッチ方式と呼ばれる新しい方式です。JITコンパイラは、CPythonのビルドにおいて--enable-experimental-jitオプションを指定した場合に有効となります。

新しいメモリアロケータ(mimalloc)

 Python 3.12まではオプション的な位置付けであったメモリアロケータライブラリmimallocが、Python 3.13では標準となりました。mimallocはme-mallocと発音し、その中身はC言語の標準メモリ割り当て関数であるmallocの置き換えです。標準のmalloc関数に比べて性能的に優れており、それを使うプログラムにも性能上のメリットをもたらします。

 Python 3.13においてmimallocが標準となったことで、メモリの確保や解放にかかわるあらゆる局面での性能向上が期待できます。

新しい型関連機能[3.13]

 Pythonでは、型安全の向上のために型関連機能が随時強化、追加されています。地味ではありますが、Python 3.13でも新しい型関連機能が追加されています。いずれも、型チェッカに有効に働く機能です。

型変数における型パラメータの既定値の導入(typing.NoDefault)

 Python 3.13では、typing.TypeVar、ParamSpec、TypeVarTupleにおける型パラメータの既定値をdefault引数によって指定することができるようになりました。この指定がない型パラメータは既定値がない状態になりますが、これを明示的に表すことができるようにするのがtyping.NoDefaultです。

 typing.NoDefaultは、typing.TypeVarのdefault引数の既定値に設定されます。つまり、明示的に初期値を与えないとtyping.NoDefaultのままとなります。既定値は、__default__で参照することができます。

リスト nodefault.py
from typing import TypeVar, NoDefault

T = TypeVar("T")
print(T.__default__ is NoDefault)	# True

S = TypeVar("S", default=None)
print(S.__default__ is None)		# True

 なお、Python 3.12で、TypeVarなどを利用しない新しい型引数構文が導入されましたが、そこでの既定値の指定は以下のようになります。

リスト nodefault.py
class Cls[T = NoDefault]:	# 既定の型を指定
    def default(self):
        print(T.__default__)

c = Cls()
c.default()	# typing.NoDefault

ユーザ定義型の関数戻り値の新しい注釈(typing.TypeIs)

 typing.TypeIsは、型の絞り込み(type narrowing)のための新しい型です。型の絞り込みとは、第3回でも少し触れた通り、型チェッカによる型ガードのためにプログラムの処理から型の情報を取得することです。これには、同じく第3回で紹介したtyping.TypeGuardを戻り値とするユーザ定義関数が使われてきましたが、typing.TypeIsは、それを適切に実装し直したものと言えます。

 TypeGuardにはいくつかの制限があり、例えば以下のリストのようなケースで、else節で型を正しく絞り込んでくれない問題がありました。

リスト typeguard.py
from typing import TypeGuard

def is_integer(x: object) -> TypeGuard[int]:
  return isinstance(x, int)

def increment(x: int | str):
  if is_integer(x):
    print(x + 1)				# xはint
  else:
    print("".join([chr(ord(e)+1) for e in x]))	# xがintでもあるとみなされる

increment(10)
increment("Python")
TypeGuardにおける絞り込みの問題
TypeGuardにおける絞り込みの問題

 実行には問題ありませんが、型チェッカにおいてはelse節のxがintでイテレータがないと指摘されます。if節でintでないことがはっきりしているのにもかかわらず、です。このように、TypeGuardはTrueの場合には型を絞り込むことができますが、Falseの場合には全く絞り込みができません。これでは役割を果たせないとして、TypeIsが新たに設けられました。使い方はほぼ同様ですが、else節が期待される動作となります。上記のリストのTypeGuardをTypeIsに置き換えるだけです(配布サンプルのtypeis.py)。

 TypeGuardの動作を変更することも検討されたようですが、TypeIsとの併存ということになったようです。Python 3.13以降では、TypeGuardよりTypeIsを優先して使うことが推奨されるでしょう。

TypedDictにおける読み出し専用要素の指定(typing.ReadOnly)

 TypedDictは、型付き辞書と呼ばれ、key-value形式のデータ構造を型付きで表現できる型です。その要素に対して、型の注釈に加え、読み出し専用であることの注釈を付加するのが、typing.ReadOnlyです。読み出し専用に指定された要素に書き込もうとすると、型チェックでエラーとすることができます。要素を読み取り専用にするには、ReadOnly[型名]を注釈として付加します。

リスト readonly.py
from typing import TypedDict, ReadOnly

class Person(TypedDict):
   name: ReadOnly[str]
   age: int

def modify_person(p: Person):
   p["age"] = 60			# 書き換えOK
   p["name"] = "Nao Yamauchi"		# 書き換え不可

 この場合、ageは書き換え可能ですが、nameは書き換えられません。イミュータブルな要素を指定できるようになり、安全性が向上します。

次のページ
モバイルプラットフォームのサポート[3.13]

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Pythonの新機能を知ろう!連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/20443 2024/11/15 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング