LoginSignup
127
80

More than 1 year has passed since last update.

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

Last updated at Posted at 2020-11-07

はじめに

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

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

それでもやはり次のリリースで何が載ってくるのかは気になるので抜粋してまとめていきたいと思います。なお、3.9の次のバージョンは4.0ではなく3.10です(笑)。まずはいつもの開発ロードマップ(PEP-619)。

  • 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: 構造的パターンマッチング

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

→ 別記事にしました: Python3.10の新機能(2) - Pythonにmatch文がやってくる

PEP 626: デバッグなどで正確な行番号を表示

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

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

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

     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()の返り値をみるとelsefinallyの行に相当する項目がないことに気がつくと思います。これらの行は実際には実行されることのない文で、その他にdelglobalnonlocalなどがそれにあたります。

PEP 563: アノテーションの遅延評価がデフォルトになる

Python-3.7の新機能として「アノテーションの評価を遅らせる」というのを紹介しましたが、その動作がデフォルトになりました デフォルトになるのは見送られました。

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

from __future__ import annotations

というimport文を入れる必要がありましたが、3.10からはその必要がなくなるはずでした。実際、α版には取り入れられたのですが、その後、過去資産で動かなくなるものが多々あるなど影響範囲が大きい事がわかり、導入が延期されたとのことです。詳細はこちらのブログ記事をご参照ください。@methaneさん、ご指摘ありがとうございました。

PEP 613: 型エイリアスのアノテーション

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

例えばこれまでは

IntType = int   

と書いていたものが

IntType: TypeAlias = int

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

PEP 604: 共用体型オペレータ

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

number: Union[int, float]

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

number: int | float

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

PEP-612: 引数仕様変数

→ 別記事にしました: Python3.10の新機能(1) - 引数仕様変数 (Parameter Specification Variables)

PEP 618: 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からエラーになります。

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

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

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

モジュールの改善

base64

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

curses

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

glob

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

ssl

OpenSSL 1.1.1以降が必須になりました(PEP-644)

types

types.EllipsisTypetypes.NoneTypetypes.NotImplementedTypeが(再)導入されました。

最適化

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

廃止予定

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

機能削除

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

まとめ

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

127
80
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
127
80