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

## はじめに

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)

リリースサイクルが1年になり、この前3.9が出たばかりと思ったらもう3.10のa2が出ていました（汗）。[ここ](https://qiita.com/ksato9700/items/a6578b09b2949c8548dd)で書いているように、正式版がでるのと同時に次のα版がでるのでまあ予定通りなのですが、ちょっとペースが早い (^^;　（←この記事を書き始めたころのコメントです。）

それでもやはり次のリリースで何が載ってくるのかは気になるので抜粋してまとめていきたいと思います。なお、3.9の次のバージョンは4.0ではなく3.10です（笑）。まずはいつもの開発ロードマップ([PEP-619](https://www.python.org/dev/peps/pep-0619/))。

* 3.10.0 開発開始: 2020-05-18 (完了)
* 3.10.0 alpha 1: 2020-10-05 (完了)
* 3.10.0 alpha 2: 2020-11-02 -> 2020-11-04 (完了)
* 3.10.0 alpha 3: 2020-12-07 (完了)
* 3.10.0 alpha 4: 2021-01-04 (完了)
* 3.10.0 alpha 5: 2021-02-03（完了）
* 3.10.0 alpha 6: 2021-03-01（完了）
* 3.10.0 alpha 7: 2021-04-06（完了）
* 3.10.0 beta 1: 2021-05-03（完了）
* 3.10.0 beta 2: 2021-05-31（完了）
* 3.10.0 beta 3: 2021-06-17（完了）
* 3.10.0 beta 4: 2021-07-10（完了）
* 3.10.0 candidate 1: 2021-08-03（完了）
* 3.10.0 candidate 2: 2021-09-07（完了）
* 3.10.0 final: 2021-10-04 （完了！）

予定通り、3.9からちょうど1年後の2021年10月に3.10がでました。

## 更新履歴

### 2021-10-04

* 2021-10-04にリリースされた 3.10.0をベースに改定しました。
  * 「PEP-626：デバッグなどで正確な行番号を表示」を追加しました
  * 「PEP-563:アノテーションの遅延評価がデフォルトになる」の導入が延期されたので記述を変更しました。

###  2021-05-04

* 2021-05-03にリリースされた 3.10 beta 1をベースに改定しました。
  * 構造的パターンマッチングについて追記
  * OpenSSL 1.1.1以上が必須な件を追記

### 2021-01-24

- 2021-01-04にリリースされた 3.10 alpha 4をベースに改定しました。
  - 廃止予定に PEP-623 (Unicode内部実装関連)を追加
  - 「PEP-612: 引数仕様変数」に関して別記事に書き、それへのリンクを追加しました

### 2020-11-07

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

## 注目した新しい機能

### [PEP 634](https://www.python.org/dev/peps/pep-0634/): 構造的パターンマッチング

Python 3.10の目玉機能だと思いますが、パターンマッチングの機能がPythonにやってきます。RustやScalaではお馴染みの機能で、switch文がないPythonにとっては今後多用されることになるのではないでしょうか。

→　別記事にしました: [Python3.10の新機能(2) - Pythonにmatch文がやってくる](https://qiita.com/ksato9700/items/3ce4c68c0d713874b693)

### [PEP 626](https://www.python.org/dev/peps/pep-0626/): デバッグなどで正確な行番号を表示

実行時エラーが起きた時にはスタックトレースが表示されますが、そこにある行番号はデバッグする上でとても重要な情報になります。これまでの実装でもほとんどの場合で正しい行番号を表示していましたが、そうではない場合もいくつかありました。それを修正するのがこのPEPです。

これまでも実行中のバイトコードに対応するソースコードの行番号はフレームオブジェクトの`f_lineno`という属性に保持されていました。それに加えて `co_lines()` というメソッドが追加され、バイトコードと行番号の関連がより明確にわかるようになっています。

例えば以下のようなコードを例に挙げてみます。便宜上、行番号も表示しています。

```python
     1	from dis import dis
     2
     3	def f(a):
     4	    try:
     5	        if a:
     6	            print("yes")
     7	        else:
     8	            print("done")
     9	    finally:
    10	        print("done")
    11
    12	if __name__ == "__main__":
    13	    code = f.__code__
    14	    print("==co_lines==")
    15	    print(list(code.co_lines()))
    16	    print("\n==bytecode==")
    17	    dis(code.co_code)
```

これを実行すると以下の出力が得られます。

```
==co_lines==
[(0, 2, 4), (2, 6, 5), (6, 16, 6), (16, 26, 8), (26, 38, 10), (38, 40, 6), (40, 62, 10)]

==bytecode==
          0 SETUP_FINALLY           25 (to 52)
          2 LOAD_FAST                0 (0)
          4 POP_JUMP_IF_FALSE        8 (to 16)
          6 LOAD_GLOBAL              0 (0)
          8 LOAD_CONST               1 (1)
         10 CALL_FUNCTION            1
         12 POP_TOP
         14 JUMP_FORWARD            11 (to 38)
    >>   16 LOAD_GLOBAL              0 (0)
         18 LOAD_CONST               2 (2)
         20 CALL_FUNCTION            1
         22 POP_TOP
         24 POP_BLOCK
         26 LOAD_GLOBAL              0 (0)
         28 LOAD_CONST               2 (2)
         30 CALL_FUNCTION            1
         32 POP_TOP
         34 LOAD_CONST               0 (0)
         36 RETURN_VALUE
    >>   38 POP_BLOCK
         40 LOAD_GLOBAL              0 (0)
         42 LOAD_CONST               2 (2)
         44 CALL_FUNCTION            1
         46 POP_TOP
         48 LOAD_CONST               0 (0)
         50 RETURN_VALUE
    >>   52 LOAD_GLOBAL              0 (0)
         54 LOAD_CONST               2 (2)
         56 CALL_FUNCTION            1
         58 POP_TOP
         60 RERAISE                  0
```

`co_lines()`は3値のタプルを返すジェネレータで、それぞれ `(バイトコードの開始オフセット、バイトコードの終了オフセット, 行番号) `となっています。したがって、1つ目の `(0, 2, 4)`は「バイトコードの`0`番目から`2`番目（の手前）までは行番号`4`」ということになります。つまりソースコードを見るとそれは `try` 文であることがわかります。つまり、

```
          0 SETUP_FINALLY           25 (to 52)
```

が`try`の実行ということになります。同様に二つ目の `(2, 6, 5)`は「バイトコードの`2`番目から`6`番目（の手前）までは行番号`5`」ということになり、

```
          2 LOAD_FAST                0 (0)
          4 POP_JUMP_IF_FALSE        8 (to 16)
```

が`if` 文の実行ということになります。

なお、`co_lines()`の返り値をみると`else`や`finally`の行に相当する項目がないことに気がつくと思います。これらの行は実際には実行されることのない文で、その他に`del`、`global`、`nonlocal`などがそれにあたります。



### [PEP 563](https://www.python.org/dev/peps/pep-0563/): ~~アノテーションの遅延評価がデフォルトになる~~

Python-3.7の新機能として「[アノテーションの評価を遅らせる](https://qiita.com/ksato9700/items/35a0bdc04693b3b09757#pep-563-%E3%82%A2%E3%83%8E%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E8%A9%95%E4%BE%A1%E3%82%92%E9%81%85%E3%82%89%E3%81%9B%E3%82%8B)」というのを紹介しましたが、~~その動作がデフォルトになりました~~ デフォルトになるのは見送られました。

これまでは遅延評価を有効にするには

```python
from __future__ import annotations
```

というimport文を入れる必要がありましたが、3.10からはその必要がなくなるはずでした。実際、α版には取り入れられたのですが、その後、過去資産で動かなくなるものが多々あるなど影響範囲が大きい事がわかり、導入が延期されたとのことです。詳細は[こちらのブログ記事](https://methane.hatenablog.jp/entry/2021/04/21/%60from___future___import_annotations%60_%E3%81%8CPython_3.10%E3%81%A7%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%AB%E3%81%AA%E3%82%89%E3%81%AA%E3%81%8F%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%97)をご参照ください。@methaneさん、ご指摘ありがとうございました。



### [PEP 613](https://www.python.org/dev/peps/pep-0613/): 型エイリアスのアノテーション

[PEP 484](https://www.python.org/dev/peps/pep-0484/)で型エイリアス（型に別名をつける）が導入されましたが、トップレベルでの代入式で表現されるので通常の変数への代入と見分けがつきにくいという問題がありました。型エイリアスであることを明確にするために`TypeAlias`というアノテーションが追加されます。

例えばこれまでは

```python
IntType = int	
```

と書いていたものが

```python
IntType: TypeAlias = int
```

という風に書けるようになります。



### [PEP 604](https://www.python.org/dev/peps/pep-0604/): 共用体型オペレータ

これまた型関係の変更ですが、2つ以上の型を取る変数の型アノテーションをする場合、これまでは

```python
number: Union[int, float]
```

と書かなければならなかったところが

```python
number: int | float
```

と書けるようになります。TypeScriptなどでも使われている記法を取り入れた感じですね。

### [PEP-612](https://www.python.org/dev/peps/pep-0612/): 引数仕様変数

→　別記事にしました: [Python3.10の新機能(1) - 引数仕様変数 (Parameter Specification Variables)](https://qiita.com/ksato9700/items/3320e5efe2c75d5cb88c)

### [PEP 618](https://www.python.org/dev/peps/pep-0618/): zip関数の追加パラメータ

`zip()`関数に`strict`というパラメータが追加され、これが`True`の時にはzipする2つのイテラブルが同じ長さであることをチェックします。長さが違う場合には `ValueError`例外が送出されます。

##その他の言語の変更

* `int`型の変数に`bit_count()`というメソッドが追加されました。これは整数の数値をバイナリ表現した場合の`1`の数を返すというものです。これは、Population Count (or pop count)という呼び方もされるようですが、基本は `bin(a).count('1')`とやっていることは同じで、より高速に処理ができるようになったということのようです。

* 辞書型のメソッド `keys()`、 `values()`、 `items()`が返すビューオブジェクトに`mapping`という属性が追加され、元の辞書型データへアクセスできるようになりました。

* DecimalおよびFractionは整数引数の値として使えなくなります。例えば以下のようなコードは Python-3.9.では `DeprecationWarning`が出されていましたが、python-3.10からエラーになります。

  ```python
  from decimal import Decimal
  i = Decimal(97)
  print(chr(i))
  ```



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

3.10では追加モジュールは無いようです。



## モジュールの改善



#### base64

[RFC-4648](https://tools.ietf.org/html/rfc4648.html#section-7)で定義されている拡張Hexアルファベットをサポートするために`base64.b43hexencode()`と`base64.b43hexdecode()`が追加されました。通常のBase32のアルファベットは他の文字との見た目が似ている '0'と'1'を使わないようになっているのですが、この拡張Hexアルファベットではそれらは使用されていて、代わりにマッピングされた文字がソート順になっているという特徴があります（それで何が嬉しいのかは謎ですが）

#### curses

ncurses 6.1 で導入された拡張カラーを使うことができるようになります。Pythonから利用しているncursesライブラリがこれをサポートしているかどうかは `curses.has_extended_color_support()`で確認することができます。

#### glob

`glob()`と`iglob()`に`root_dir`と`dir_fd`パラメータが追加され、ファイル検索のルートディレクトリを指定できるようになりました。前者はパス風オブジェクトで、後者はルートディレクトリのファイルディスクリプタを指定します。

#### ssl

OpenSSL 1.1.1以降が必須になりました（[PEP-644](https://www.python.org/dev/peps/pep-0644/))

#### types

`types.EllipsisType`、`types.NoneType`、`types.NotImplementedType`が（再）導入されました。

## 最適化

* `str()` `bytes()` `bytesarray()`が速くなりました（小さなオブジェクトの場合30-40%）
* `runpy`モジュールがインポートするモジュールの数が少なくなり、`python -m モジュール名`で実行した場合に起動時間が平均で 1.3倍速くなりました

## 廃止予定

* このバージョンから、python2.7との互換性のために残されていた古いインポート方法が廃止予定になります。
* `find_loader()`、`find_module()`、 `load_module()`、`__package__`属性、`__loader__`属性は将来のバージョンで消去されます。
* Unicodeの内部実装に手が入り、[PEP-393](https://www.python.org/dev/peps/pep-0393/)で導入されたC言語のAPIとそれらのAPIで使われていたunicodeオブジェクト内のデータが廃止予定になります。元々はPython 4.0で削除予定でしたが、4.0のスケジュールが立っていないため、3.12で削除することになりました。これによって文字列一つあたり8バイトの節約になりAPI呼び出時のオーバーヘッドも削減できます ([PEP-623](https://www.python.org/dev/peps/pep-0623/))


## 機能削除

- `complex` クラスの`__int__`, `__float__`, `__floordiv__`, `__mod__`, `__divmod__`, `__rfloordiv__`, `__rmod__` , `__rdivmod__ `メソッドが消去されました。

## まとめ

Python 3.10の変更点についてまとめてみました。b1になったので仕様はこれでFixだと思います。紹介しきれていない変更についてまた後ほど追記するかも知れませんが、3.11の開発も始まりますし、そちらも徐々に追いかけていきたいと思います。