Python3.12の新機能 (まとめ)

## はじめに

Python 3.5から What's Newの内容をまとめる記事を投稿してきました。

* [Python3.5の新機能](http://qiita.com/ksato9700/items/9a0aef07d90d00e67525)
* [Python3.6の新機能](https://qiita.com/ksato9700/items/ed839a6db6a671fd31e6)
* [Python3.7の新機能](https://qiita.com/ksato9700/items/35a0bdc04693b3b09757)
* [Python3.8の新機能(まとめ)](https://qiita.com/ksato9700/items/3846e8db573a07c71c33)
* [Python3.9の新機能(まとめ)](https://qiita.com/ksato9700/items/d5df9d36147796c86c77)
* [Python3.10の新機能(まとめ)](https://qiita.com/ksato9700/items/2cd85bec2ae4c1785238)
* [Python3.11の新機能(まとめ)](https://qiita.com/ksato9700/items/e2356ea9c19c3ff8a467)

リリース前に次の新機能を先取り的に紹介を始めて今回で8回目です。最初に始めた頃はこんなに長く続けるとは思っていませんでしたが、年中行事になっているので今回もまとめていきます。

次は3.12で、開発ロードマップ([PEP-693](https://www.python.org/dev/peps/pep-0693/))はこの様になっています。

* 3.12.0 開発開始: 2022-05-08 (完了)
* 3.12.0 alpha 1: 2022-10-24 (完了)
* 3.12.0 alpha 2: 2022-11-14 (完了)
* 3.12.0 alpha 3: 2022-12-06 (完了)
* 3.12.0 alpha 4: 2023-01-10 (完了)
* 3.12.0 alpha 5: 2023-02-07 (完了)
* 3.12.0 alpha 6: 2023-03-07 (完了)
* 3.12.0 alpha 7: 2023-04-03
* 3.12.0 beta 1: 2023-05-08 (これ以降は新たな機能の追加は行わない)
* 3.12.0 beta 2: 2023-05-29
* 3.12.0 beta 3: 2023-06-19
* 3.12.0 beta 4: 2023-07-10
* 3.12.0 candidate 1: 2023-07-31
* 3.12.0 candidate 2: 2023-09-04
* 3.12.0 final: 2023-10-02

今回は書き初めが少し遅くなってしまって、すでにalpha 6が出ています。ベータまであと2ヶ月弱で機能の実装の方もだいぶ進んでいるのではないかと思います。

## 更新履歴

### 2022-03-12

- 最初のバージョン。a6が 2023-03-にリリースされましたが、そのwhat's new をベースに書いています。

## 注目した新しい機能

### エラーメッセージの改善

- トップレベルで`NameError`を出す時に、対象の名前が標準ライブラリ名の場合は「もしかしてインポートし忘れている？」というメッセージを出すようになりました。

3.11以前

```python
>>> sys.version_info
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined
```

3.12以降

```python
>>> sys.version_info
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined. Did you forget to import 'sys'?
```

- メソッド内で`NameError`を出す時に、対象の名前がそのインスタンスの属性名の場合には「もしかして、selfをつけ忘れてる？」というメッセージを出すようになりました。

```python
>>> class A:
...     def __init__(self):
...        self.blech = 1
...     def foo(self):
...        somethin = blech
...
>>> A().foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in foo
NameError: name 'blech' is not defined. Did you mean: 'self.blech'?
```

- `from y import x` を`import x from y`と間違えて書いた時の `SyntaxError`で「もしかして from ... import って書こうとした？」というメッセージを出すようになりました。

```python
>>> import a.y.z from b.y.z
  File "<stdin>", line 1
    import a.y.z from b.y.z
    ^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: Did you mean to use 'from ... import ...' instead?
```



- `from <module> import <name>`で名前が見つからずにImportError`を出す時に「もしかしてこのこと？」というメッセージが出るようになりました。

```python
>>> from collections import chainmap
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'chainmap' from 'collections'. Did you mean: 'ChainMap'?
```



### Linux perfプロファイラーのサポート

`-Xperf`オプションをつけるか、`PYTHONPERFSUPPORT`環境変数をセットして実行すると、`perf record`コマンドで作成するのと同じperf.dataファイルを生成できます。これを `perf report`コマンドにかけるとプロファイル結果を見ることができます。また``sys.activate_stack_trampoline()`と`sys.deactivate_stack_trampoline()`を使ってプログラム内でプロファイルデータの生成を明示的に書くこともできます。

## その他の言語の変更

- `types.MappingProxyType`型のインスタンスは元のマッピングがハッシュ可能であればハッシュ可能になります。

- `memoryview`で2バイト浮動小数点型を扱えるようになります (フォーマットコード “e”)。

- NULLバイトを含む含むソースコードは `SyntaxError`になります。

- NULLバイトを含むソースコードをパースすると `ast.parse()` は`ValueError`の代わりに`SyntaxError`を返すようになります。

- ガベージコレクション（GC）の動作タイミングが変わり、オブジェクトの割り当て時ではなく、Pythonバイトコード評価ループ中の「評価ブレーカー」メカニズムで実行されます。加えて、Pythonコードを実行せずに長時間動作するC言語拡張でもGCが呼ばれるように、`PyErr_CheckSignals()` が呼ばれるタイミングでも実行されます。

- 正しいエスケープシーケンスではないバックスラッシュと文字列のペアは `DeprecationWarning`ではなく`SyntaxWarning`を発生させることになります。例えば、`re.compile("\d+\.\d+")`は`SyntaxWarning`を出すので、正規表現には`re.compile(r"\d+\.\d+")`という風にraw文字列を使ってください。将来的には`SyntaxWarning`ではなく`SyntaxError`が出されることになります。

- `0o377`以上の8進数えスケープ（例えば "\477"）は`SyntaxWarning`を出すようになります。これも将来的には`SyntaxError`になります。

- ブール型を引数に取る全ての組み込み関数、拡張関数はブール型、整数型だけでなくその他の型の値も引数として受けとれるようになります。

- 内包表記の中で代入式を使えるようになります

- スライスオブジェクトがハッシュ可能になり、辞書型のキーやセットのアイテムとして使えるようになります。

## 新規に追加されたモジュール

（まだありません）



## モジュールの改善

#### array

- `array.array`でタイプ指定ができるようになりました（例えば `array.array[int]`のように）

#### asyncio

- Linuxでは、`os.pidfd_open()`が利用可能な場合は、`asyncio`はデフォルトで`ThreadedChildWatcher`の代わりに`PidfdChildWatcher`を使うようになりました。

- 子プロセスウォッチャーの`MultiLoopChildWatcher`、 `FastChildWatcher`、 `AbstractChildWatcher`、 `SafeChildWatcher`は廃止予定となり、Python 3.14で削除されます。子プロセスウォッチャーをマニュアルで設定することは推奨されず、イベントループが各プラットフォームで最適な子プロセスウォッチャーを自動で選びます（`PidfdChildWatcher`が利用可能なときはそれを使い、そうでなければ `ThreadedChildWatcher` を使います）。
- `asyncio.set_child_watcher()`、 `asyncio.get_child_watcher()`、 `asyncio.AbstractEventLoopPolicy.set_child_watcher()`、 `asyncio.AbstractEventLoopPolicy.get_child_watcher()`は廃止予定となり、Python 3.14で削除されます。
- カスタムのイベントループファクトリーを設定できるようにするために、*loop_factory*パラメータが`asyncio.run()`に追加されます。
- C言語実装の `asyncio.current_task()`が追加され、4~6倍の速度向上を得られます。

#### pathlib

- `walk()`が追加され、ディレクトリーツリーを走査してその配下の全てのディレクトリとファイルのリストを作ります。
- `pathlib.PurePath.relative_to()`のオプショナルパラメーターとして *walk_up* が追加され、`..`を含む結果を得ることができます。
- `os.path.isjunction()`の代替として、`pathlib.Path.is_junction()`が追加されます。



## 最適化

* [PEP 623](https://peps.python.org/pep-0623/) Unicodeからwstrを削除
  * Python 3.3でUnicodeの幾つかのAPIを削除した際に、Unicodeオブジェクトに `wstr`と` wstr_length`というメンバーを追加しました。Python 3.12でそれが削除されます。それによって文字列ひとつあたり8バイトのメモリーが節約できます。また、Unicode関連APIの呼び出し時のオーバーヘッドも削減できます。



## 廃止予定

- `typing.Hashable`と `typing.Sized`は `collections.abc.Hashable`と `collections.abc.Sized`のエイリアスになります。


## 機能削除

- [PEP 632](https://peps.python.org/pep-0632/), `distutils`パッケージの削除
  - `disutils`は標準ライブラリに含まれていましたが、`setuptools`経由で使うことを勧められてきました。そして、`setuptools`は `distutils`相当の機能を自分で持ってしまっていて、`distutils`への依存関係がなくなっています。さらに、`pip`は`distutils`のインストール要求を暗黙のうちに`setuptools`とすり替えてしまいます。ということで、すでに `distutils`を使っている人はいないので削除することになりました。

## まとめ

Python 3.12の変更点についてまとめてみました。5月のベータリリースまでは機能追加が続くのでこれから何度かアップデートしていくことになると思います。また、面白そうな新機能があれば別記事にするかも知れません。