What’s New In Python 3.12 ノート¶
What’s New In Python 3.12 をたどりながら調査。興味のあるものしか読まない。
New Features¶
PEP 695: Type Parameter Syntax¶
Python の術語では generic というのだが、C++ の関数テンプレートやクラステンプレートに相当するコードの記法が改善した。例えば次のようなコードが合法になる:
std::minmax のような Python 関数¶def minmax[T](a: T, b: T) -> (T, T):
return (b, a) if b < a else (a, b)
さらに、TypeAliasType のオブジェクトを生成する type 文を使用して、型別名を宣言する新しい方法が導入された:
type 文を用いた型別名の例¶type Point2D = tuple[float, float]
type Point3D[T] = tuple[T, T, T]
TypeVarTuple と ParamSpec 宣言する新文法:
type による TypeVarTuple および ParamSpec の宣言例¶type IntFunc[**P] = Callable[P, int] # ParamSpec
type LabeledTuple[*Ts] = tuple[str, *Ts] # TypeVarTuple
一行目は P = typing.ParamSpec('P') を含意する。ただし P というオブジェクトを生成するわけではない。IntFunc が「単品」で使える。
二行目は Ts = typing.TypeVarTuple('Ts') を含意する。ただし Ts というオブジェクトを生成するわけではない。
また、束縛や制約を持つ TypeVar も同様に宣言可能だ:
type による束縛 TypeVar および制約 TypeVar の宣言例¶type HashableSequence[T: Hashable] = Sequence[T] # TypeVar with bound
type IntOrStrSequence[T: (int, str)] = Sequence[T] # TypeVar with constraints
一行目は T = typing.TypeVar('T', bound=typing.Hashable) を含意するが T
そのものとしては生成しない。
同様に、二行目は T = typing.TypeVar('T', int, str) を含意するが T そのものは生成しない。
PEP 701: Syntactic formalization of f-strings¶
引用符の再利用
以前は f-string を囲む引用符と同じ記号を再利用すると SyntaxError が送出していた。例えば f"..." では ... の部分に " を入れることはできなかった。これが可能になった。
In [1]: things = []
In [2]: f"These are the things: {", ".join(things)}"
Out[2]: 'These are the things: '
そして、f-strings は有効な Python 式を式部品の中に含むことができるようになったので、f-strings を入れ子にすることができるようになった。
複数行の式とコメント
以前は f-string 式は一行で定義する必要があった。Python 3.12 では複数行にわたる f-string を定義し、インラインコメントを追加できるようになった。
バックスラッシュと Unicode 文字
以前の f-string 式はどんなバックスラッシュ文字をも含むことが不可能だった。特に、 Unicode エスケープシーケンスに悪影響があった。
In [1]: a = ["Hello", "world"]
In [2]: f"{"\n".join(a)}"
Out[2]: 'Hello\nworld'
エラーメッセージの精度向上
以上の文字列解析機能強化により、副産物として f-string のエラーメッセージがより正確になり、エラーの正確な場所が含まれるようになった。
PEP 688: Making the buffer protocol accessible in Python¶
メソッド __buffer__() を実装したクラスがバッファー型として使用できる。このメソッドはオブジェクトに内在する記憶域を開陳するオブジェクトを返す。
このようなオブジェクトの論理的な基底型が collections.abc.Buffer だ。これは型註釈などでバッファーオブジェクトを表現する標準的な方法を搭載している。
collections.abc.Buffer¶def need_buffer(b: Buffer) -> memoryview:
return memoryview(b)
need_buffer(b"xy") # ok
need_buffer("xy") # rejected by static type checkers
Buffer はバッファー型を判定するために isinstance() と issubclass()
でも使える。
独自バッファー実装例は <https://peps.python.org/pep-0688/> を見ろ。
PEP 709: Comprehension inlining¶
内包記法周りが主に速度の点で強化。
Improved Error Messages¶
よりわかりやすいメッセージに改良。
Other Language Changes¶
ヌル文字を含むソースコードを解析すると
SyntaxErrorが生じる。有効なエスケープシーケンスではないバックスラッシュ文字のペアが生じるのは
DeprecationWarningではなくSyntaxWarningに変更した。正規表現を書いているときなどに出くわすかもしれない。What’s New In Python 3.11 ノート であったように、数
0o377より大きな値の八進数エスケープが生じる例外がDeprecationWarningからSyntaxWarningに変更した。いずれエラーになる予定とのこと。try-except*構文がExceptionGroup全体を処理し、他の例外を一つ発生させる場合、その例外はExceptionGroupにラップされなくなった。バージョン 3.11.4+ にもこの変更が適用された。引数として
TrueかFalseを期待する組み込み関数全てと拡張関数の callable 全てが、boolとintだけでなく、任意の型の引数を受け付けるようになった。明示的に真偽値にキャストしなくて済むということだから便利だ。sum()で Neumaier 和を採用。浮動小数点数またはintと浮動小数点数の混合を合計する際の精度と可換性が向上した。 Improve accuracy of builtin sum() for float inputs を見ろ。And more?
New Modules¶
何もない。
Improved Modules¶
array¶
クラス array が添字対応し、汎用型になる。以前は a[0] などが書けなかったということか?
asyncio¶
イベントループは各プラットフォームで利用可能な最適の見張りを使用するようになった。手動で見張りを設定することは勧められない。
run()にloop_factory引数が追加。自家製イベントループ工場を指定可能。iscoroutine()はasyncioがジェネレーターベースのコルーチンを支持しないため、ジェネレーターに対してFalseを返すように変更した。wait()とas_completed()はジェネレーターがyieldするタスクを受け付けるようになった。
calendar¶
列挙型 Month および Day が追加。一年の月と曜日を定義する。
calendar.Month and calendar.Day¶In [1]: from calendar import Month, Day
In [2]: Month
Out[2]: <enum 'Month'>
In [3]: list(Month)
Out[3]:
[calendar.JANUARY,
calendar.FEBRUARY,
calendar.MARCH,
calendar.APRIL,
calendar.MAY,
calendar.JUNE,
calendar.JULY,
calendar.AUGUST,
calendar.SEPTEMBER,
calendar.OCTOBER,
calendar.NOVEMBER,
calendar.DECEMBER]
In [4]: list(Day)
Out[4]:
[calendar.MONDAY,
calendar.TUESDAY,
calendar.WEDNESDAY,
calendar.THURSDAY,
calendar.FRIDAY,
calendar.SATURDAY,
calendar.SUNDAY]
利用者ノート
月曜の値は 0 であることを確認。
csv¶
フラグに QUOTE_NOTNULL と QUOTE_STRINGS が追加。
QUOTE_NOTNULL出力の場合、
Noneでないフィールド全てを引用符で囲む。QUOTE_ALLと似ているようだがフィールド値がNoneである場合に、空の(引用符で囲まれていない)文字列を書き込むという点が異なる。空(引用符で囲まれていない)フィールドをNoneだと解釈し、それ以外はQUOTE_ALLと同じ動作をする。入力の場合、空の(引用符で囲まれていない)フィールドを
Noneとして解釈し、それ以外はQUOTE_ALLとして動作する。QUOTE_STRINGS出力の場合、文字列フィールドを常に引用符で囲む。
QUOTE_NONNUMERICに似ているようだが、フィールド値がNoneである場合に、空の(引用符で囲まれていない)文字列を書き込むという点が異なる。入力の場合、空の(引用符で囲まれていない)文字列を
Noneだと解釈し、それ以外はQUOTE_NONNUMERICとして動作する。
fractions¶
Fraction 型のオブジェクトが関数 format() などでの浮動小数点数書式に対応した。
In [1]: from fractions import Fraction as F
In [2]: x = F(1, 8)
In [3]: f'{x:.3e} {x:.3f} {x:.3g} {x:.3%}'
Out[3]: '1.250e-01 0.125 0.125 12.500%'
itertools¶
ジェネレーター batched() が追加。反復可能なデータを指定長の tuple に一括する。これはコード片を見るほうが理解が早い:
batched()¶def batched(iterable, n):
# batched('ABCDEFG', 3) → ABC DEF G
if n < 1:
raise ValueError('n must be at least one')
iterator = iter(iterable)
while batch := tuple(islice(iterator, n)):
yield batch
math¶
関数 sumprod(p, q) 追加。ドット積。
関数 nextafter(x, y) を拡張。一度に複数のステップを上下に移動するための
steps 引数を追加。拡張動機はこういうコードを書かざるを得なかったことがあったかららしい:
x = nextafter(nextafter(nextafter(x, inf), inf), inf)
os, os.path¶
Windows に関係する新機能が多いようなので割愛。
pathlib¶
ディレクトリー木を走査し、その中のファイルまたはディレクトリーすべての名前を生成するためのメソッド walk() がクラス Path に追加した。動作は関数
os.walk() と同様。
PurePath.relative_to() に walk_up オプション引数が追加。結果に .. が入ることが許される。この動作は関数 os.path.relpath() と整合する。
p.relative_to(other, walk_up=True)¶In [1]: from pathlib import PurePosixPath
In [2]: p = PurePosixPath('/etc/passwd')
In [3]: p.relative_to('/usr', walk_up=True)
Out[3]: PurePosixPath('../etc/passwd')
pdb¶
デバッグセッションのために一時的に値を保持し、現在のフレームや戻り値のような値に素早くアクセスできる簡易変数が追加。
一時的なグローバル変数を設定するには、例えば、$foo = 1 とすると、デバッガーセッションで使用できるグローバル変数 $foo を設定する。簡易変数はプログラムの実行が再開されると消去されるので、foo = 1 のような通常の変数を使う場合に比べてプログラムに支障をきたす可能性は低くなる。
簡易変数が三つ、あらかじめ用意されている:
簡易変数 |
意味 |
|---|---|
|
デバッグしている現在のフレーム |
|
フレームが return している場合、その戻り値 |
|
フレームが例外を raise している場合、その例外 |
random¶
二項分布 binomialvariate(n=1, p=0.5) 追加。各試行での成功確率を p とし、独立した n 回の試行の成功回数を返す。
指数分布 expovariate() に既定引数 lambd=1.0 が追加。
shutil¶
rmtree() に引数 onexc が追加。これは onerror のようなエラーハンドラーであるが、例外オブジェクトを受け取るものだ。
sqlite3¶
なんと CLI が実装された。
$ python -m sqlite3
sqlite3 shell, running on SQLite version 3.45.3
Connected to a transient in-memory database
Each command will be run using execute() on the cursor.
Type ".help" for more information; type ".quit" or CTRL-D to quit.
sqlite>
statistics¶
関数 correlation() が拡張。ランク付けされたデータの Spearman 相関を計算するメソッド ranked を追加: correlation(x, y, method='ranked').
sys¶
今回は割愛。
tempfile¶
関数 mkdtemp(suffix=None, prefix=None, dir=None) は dir 引数が相対パスであっても、絶対パスを常に返す。
typing¶
Todo
例によって難しいから後にする。
unicodedata¶
Unicode データベースがバージョン 15.0.0 に更新した。
読者ノート
個人的には使いたい文字がないので軽視する。
unittest¶
コマンドラインオプション --durations=N は最も遅いテストケース N 個を示す。
uuid¶
uuid CLI¶$ python -m uuid
8cb09e58-2154-4b36-9bea-2806403d956e
読者ノート
これは使う可能性が高いから忘れないでおく。
Optimizations¶
ここは一般プログラマーには重要ではないかもしれない。ほとんどチェックしていない。
Deprecated¶
古いやり方をいつまで経っても採用し続けないように、一通りチェックする。
定数 calendar.January および calendar.February が咎められる。それぞれ列挙型メンバー calendar.JANUARY および calendar.FEBRUARY に置き換えろ。
クラス datetime.datetime で utcnow() と utcfromtimestamp() は将来のバージョンで削除される予定。代わりに:
In [1]: from datetime import datetime, timezone
In [2]: datetime.now(timezone.utc)
Out[2]: datetime.datetime(2024, 9, 26, 2, 26, 26, 338492, tzinfo=datetime.timezone.utc)
In [3]: import time
In [4]: datetime.fromtimestamp(time.time(), timezone.utc)
Out[4]: datetime.datetime(2024, 9, 26, 2, 28, 47, 722403, tzinfo=datetime.timezone.utc)
関数 suhtil.rmtree() の onerror 引数。代わりに onexc 引数を使え。
sys.last_type, sys.last_value, sys.last_traceback は咎められる。代わりに sys.last_exc を使え。
typing.
typing.Hashable と typing.Sized はそれぞれ collections.abc.Hashable
と collections.abc.Sized の別名だった。
Python 3.9 から使用を咎められる typing.ByteString は、使用時に
DeprecationWarning を送出する。
次の非同期系送出関数。それぞれの単一引数版を使え:
coroutine.throw(type[, value[, traceback]])generator.throw(type[, value[, traceback]])coroutine agen.athrow(type[, value[, traceback]])
bool 値に対するビット反転演算子。Python 3.16 からはエラー。代わりに演算子
not を使え。
読者ノート
~True と ~False の値がそれぞれ -2, -1 になる。
Removed¶
configparser¶
ParsingError から filename 属性・引数がなくなった。代えて source 属性・引数を使え。
クラス SafeConfigParser はもはや存在しない。代わりに、より短い名前
ConfigParser を使え。
クラス ConfigParser からメソッド readfp() が消えた。代わりに
read_file() を使え。
読者ノート
手許のコードを確認したが、影響はないようだ。
distutils¶
パッケージ全体が削除された。
unittest¶
クラス TestCase のかなりのメソッド別名が取り除かれた。量が多いのでいちいち確認するしかないが、例えば assertEquals() は assertEqual() と改めろ。
テストコードが古い場合、道具を使って置換するといい: <https://github.com/isidentical/teyit>
Porting to Python 3.12¶
本節以降、個人的には対応項目なし。
Changes in the Python API¶
正規表現における数値グループ参照とグループ名に対して、数値参照として受け入れられるのは ASCII 数字の並びだけになった。<https://github.com/python/cpython/issues/91760> 参照。
関数 random.randrange() で、実引数が正の整数としては怪しい値 (e.g. 10.2) だと TypeError を送出するようになった。
興味がある項目は以上?