What’s New In Python 3.12 ノート¶
What’s New In Python 3.12 をたどりながら調査。興味のあるものしか読まない。
New Features¶
PEP 695: Type Parameter Syntax¶
Python の術語では generic というのだが、C++ の関数テンプレートやクラステンプレートに相当するコードの記法が改善した。例えば次のようなコードが合法になる:
def minmax[T](a: T, b: T) -> (T, T):
return (b, a) if b < a else (a, b)
さらに、TypeAliasType
のオブジェクトを生成する type
文を使用して、型別名を宣言する新しい方法が導入された:
type Point2D = tuple[float, float]
type Point3D[T] = tuple[T, T, T]
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 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
だ。これは型註釈などでバッファーオブジェクトを表現する標準的な方法を搭載している。
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
が追加。一年の月と曜日を定義する。
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
に一括する。これはコード片を見るほうが理解が早い:
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()
と整合する。
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
¶
$ 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
を送出するようになった。
興味がある項目は以上?