Python学習チャンネル by PyQ

Pythonのオンライン学習プラットフォームPyQのオフィシャルブログです

2022年10月に正式版が公開されるPython 3.11について紹介します

Pythonの最新版 Python 3.11は、2022年10月に正式リリース予定です。

本記事では、Python3.11の更新内容を紹介します。

Pythonのリリースについて

Pythonの最新版リリースは、PEPで正式版までのリリーススケジュールが公開され、概ねそのスケジュールに沿って開発が進められてます。
Python 3.11は2022年7月5日現在beta 3が公開されており、beta 4以降のスケジュールは PEP664 に下記のように記載されています。

  • 3.11.0 beta 4: Thursday, 2022-06-16
  • 3.11.0 beta 5: Saturday, 2022-07-09
  • 3.11.0 candidate 1: Monday, 2022-08-01
  • 3.11.0 candidate 2: Monday, 2022-09-05
  • 3.11.0 final: Monday, 2022-10-03

candidateというのは正式版になる予定のバージョンのことです。
candidate後はバグフィックスのみで機能の変更は原則行われないので、candidateでも新機能を試すのには十分です。

PEP(Python Enhancement Proposals)

Pythonの機能やプロセス・環境などについて大きな変更があるときに書かれる提案書です。
PEPには、Pythonの機能が拡張される際の根拠や例となるコードも書かれています。

それでは、今回はPython 3.11の更新内容を4つ紹介します。

Faster CPython

Python 3.11はPython 3.10に比べて最大10-60%高速化されます。

pyperformance によるベンチマークで、 平均1.25 倍のスピードアップが計測されたそうです。

高速化のために既存のコードを修正する必要はありません。
Python 3.11にするだけで、速度が改善します。

Enhanced error locations in tracebacks

Python 3.11ではトレースバックが改善され、どこでエラーが発生したのか、より具体的な箇所が示されます。

たとえば、以下のような2点間の距離を軸毎に求めるプログラム(バグあり)を例に、実行結果をPython 3.10と比較してみましょう。

def distance(p1, p2):
    dx = abs(p1["x"] - p2["x"])
    dy = abs(p1["y"] - p2["y"])
    return {"x": dx, "y": dy}


point_1 = {"x": 50, "y": 60}
point_2 = {"x": 10, "z": 20}

d = distance(point_1, point_2)

このプログラムをPython 3.10で実行すると、以下のようなメッセージが表示されます。

Traceback (most recent call last):
  File "/Users/pyq/scripts/distance.py", line 10, in <module>
    d = distance(point_1, point_2)
  File "/Users/pyq/scripts/distance.py", line 3, in distance
    dy = abs(p1["y"] - p2["y"])
KeyError: 'y'

10行目と3行目でエラーが発生した、ということが表示されています。

次にPython 3.11 で実行すると、以下のようなメッセージが表示されます。

  File "/Users/pyq/scripts/distance.py", line 10, in <module>
    d = distance(point_1, point_2)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pyq/scripts/distance.py", line 3, in distance
    dy = abs(p1["y"] - p2["y"])
                       ~~^^^^^
KeyError: 'y'

このように行だけではなく、どの位置でエラーが発生したのかも表示してくれるようになりました。

今回の場合は、p2["y"]の箇所でKeyErrorになったと表示されています。
そこでpoint_2を確認してみると、辞書データの中に"y"というキーがなかったことに気付きます。

エラーが発生したとき、よりデバッグしやすくなりますね。

PEP 655: Marking individual TypedDict items as required or potentially-missing.

Python 3.8で導入されたtypingモジュールのTypedDictを使うと、辞書のキーを固定できますが、キー毎に必須か・必須でないかの設定まではできませんでした。

Python 3.11ではTypedDictのキー毎に、必須か・必須でないかを設定できるようになりました。

説明のために、「title」「year」という2つのキーを持ち「title」のみ必須、という辞書を考えてみたいと思います。

まずTypedDictを使って、「title」「year」という2つのキーを持つ辞書型を定義すると、以下のように書けます。

from typing import TypedDict


class Movie(TypedDict):
    title: str
    year: int

このMovieを型ヒントに指定した場合、以下のように「title」「year」の両方をキーに持つ辞書であれば問題ありません。

# OK
bttf: Movie = {"title": "バック・トゥ・ザ・フューチャー", "year": 1985}

しかし、TypedDictはデフォルトで全項目が必須となるため、以下のように「year」がない辞書は型チェックでNGとなります。

# NG: yearがない
bttf2: Movie = {"title": "バック・トゥ・ザ・フューチャー PART2"}

そこで、Python3.11から導入される NotRequiredを使い、Movieを修正します。

from typing import TypedDict, NotRequired


class Movie(TypedDict):
    title: str
    year: NotRequired(int)

これにより「year」が必須ではない項目となるため、以下のように書いても問題なくなります。

# OK: yearは必須ではない
bttf2: Movie = {"title": "バック・トゥ・ザ・フューチャー PART2"}

TypedDictがより使いやすくなりそうです。

PEP 673: Self type.

Python 3.11では、 typingモジュールにSelfが追加されます。
このSelfを使うと、メソッドが自身のインスタンスを返す場合、よりシンプルに型ヒントを記述できます。

以下のクラスを例に説明します。

class Shape:
    def set_scale(self, scale: float) -> Shape:
        self.scale = scale
        return self


class Circle(Shape):
    def set_radius(self, radius: float) -> Circle:
        self.radius = radius
        return self

このクラスを使い、以下のようなコードを書いてみます。

Circle().set_scale(0.5).set_radius(2.7)

これは、実行時には問題なく動作しますが、型チェックではエラーとなります。

Circle().set_scale(0.5)Shapeクラスのインスタンスを返していることになっているため、.set_radius(2.7)の部分で「Shapeset_radiusという属性はない」というエラーになるのです。

この回避策として、これまでは TypeVarを使って以下のように書くしかありませんでした。

from typing import TypeVar

# Shapeとそのサブクラスを表す型変数
TShape = TypeVar("TShape", bound="Shape")


class Shape:
    def set_scale(self: TShape, scale: float) -> TShape:
        self.scale = scale
        return self


class Circle(Shape):
    def set_radius(self, radius: float) -> Circle:
        self.radius = radius
        return self

しかし、この書き方は冗長で、直感的でもありません。

そこで、Python 3.11ではSelfが導入され、以下のように書けるようになりました。

from typing import Self


class Shape:
    def set_scale(self, scale: float) -> Self:
        self.scale = scale
        return self


class Circle(Shape):
    def set_radius(self, radius: float) -> Self:
        self.radius = radius
        return self

これにより、Shape().set_scale(0.5)と書いた場合はShapeクラスのインスタンスを返すことを表し、Circle().set_scale(0.5)と書いた場合はCircleクラスのインスタンスを返すことを表せます。

自身のインスタンスを返すメソッドの型ヒントを、よりシンプルかつ直感的に書けますね。

その他の変更点

Python 3.11では今回紹介した4つ以外にも、さまざまな更新があります。

詳細は Python 3公式ドキュメント - What's New In Python 3.11 をご確認ください。

Copyright ©2017- BeProud Inc. All rights reserved.