SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Pythonの新機能を知ろう!

最新版Python3.12を使いこなす! 型引数や文法関連の新機能を3.10からの変遷で紹介

Pythonの新機能を知ろう! 第3回

  • X ポスト
  • このエントリーをはてなブックマークに追加

ジェネリックなクラスと関数がシンプルな型引数構文で作成可能に

 Python 3.12では、ジェネリックなクラスや関数の定義が多くの型付け言語(Java、TypeScriptなど)と同様にシンプルな構文で記述できるようになりました。

TypeVarやGenericを用いたジェネリクス

 Python 3.12より前のジェネリクスについては、本連載第2回の「ジェネリクスとは?」で軽く紹介しました。以下のリストは、Python 3.11でジェネリクスな関数とクラスを定義する例です。

generic_typevar.py
from typing import TypeVar, Generic	(1)

T = TypeVar("T")			(2)

def function(x: T, y: T) -> T:		(3)
    return x

class MyClass(Generic[T]):		(4)
    def method(self) -> T:
        ...

 (1)のように、TypeVarやGenericといったクラスをインポートする必要があります。

 (2)のように、実際の型を格納する型変数Tをグローバルに宣言する必要があります。

 (3)ではジェネリックな関数を定義していますが、このとき型変数Tが型ヒントに使われます。

 (4)ではジェネリックなクラスを定義していますが、このときGenericクラスの継承とともに型変数Tが型ヒントに使われます。

 ここから、開発者の間では以下のような点が指摘されてきたようです。

  • typingモジュールからTypeVar、Genericなどのインポートが必要
  • 型変数の宣言が必要
  • 型変数がグローバルに宣言されることでのコンテキストの問題

 極めて基本的な機能にもかかわらず、言語仕様に組み込まれていないため、モジュールのインポートが常に必要です。また、ジェネリック型を型変数として宣言する必要があります(変数名とTypeVarの引数が同一でなければならない点も冗長といえます)。さらに、関数定義とクラス定義で同一の型変数を使っていますが、これは本来は別のものであるべきで、その場合には型変数を区別できる形でいくつも宣言する必要があります。

新しい型引数構文

 このような背景を受けて、Python 3.12では非常にシンプルな型引数構文でジェネリックな関数やクラスを定義できるようになりました。本連載でこれまで紹介してきたTypeVarなどによる型変数の宣言や、継承元のGenericクラスが必要なく、シンプルに記述できます。以下の2つのリストは、関数の定義を型引数構文の利用の有無で比較しています(import文などの違いを明確にするために別ファイルにしています)。

genericfunc_typevar.py
from typing import TypeVar

T = TypeVar("T")

def function(x: T, y: T) -> T:
    return x
genericfunc_typeparam.py
def function[T](x: T, y: T) -> T:
    return x

 TypeVarのimport文と型変数の宣言が不要になるので、非常にシンプルになります。クラスも同様です。以下の2つのリストは、クラスの定義を型引数構文の利用の有無で比較しています(import文などの違いを明確にするために別ファイルにしています)。

genericclass_typevar.py
from typing import TypeVar, Generic, Iterator

T = TypeVar("T")

class MyClass(Generic[T]):
    def method1(self) -> T:
        ...
genericclass_typeparam.py
class MyClass[T]:
    def method(self) -> T:
        ...

 記述がシンプルになることの他に、指定した型引数はそのコンテキスト内でしか有効でなくなるので、型変数の名前の衝突などを気にする必要がなくなります。

TypedDictの使用で、**kwargsの型付けをより厳密に

 関数の引数**kwargs(可変長のキーワード引数)に対する型ヒントは、全てのキーワード引数が同じ型でなければならないという制約がありました。例えば以下のリストでは、関数の引数に型ヒントstrを指定しているので、全てのキーワード引数の型がstrであると解釈され、型が異なる引数があれば型チェックでエラーとなります。逆に、関数が想定していない引数が与えられてもエラーにはなりません。

kwargs_typed.py
def function(**kwargs: str):        # すべてのキーワード引数はstr
    for key, value in kwargs.items():
        print(f"{key}: {value}")

function(name='Nao', birth=1980)    # birthに整数リテラルを割り当てることはできない
function(name='Nao', gender='m')    # genderを引数に与えてもエラーにならない

 このような制約で型ヒントの恩恵も受けづらいということで、Python 3.12ではTypedDict(型付き辞書)を使って引数ごとの型ヒントの指定が可能になりました。以下のリストのように、要素と型を列挙した辞書を作成し、**kwargsの型ヒントにUnpackして指定するだけです。辞書にある引数は全て与える必要があり、辞書にない引数を与えると、型チェックでエラーになります。

kwargs_typeddict.py
from typing import TypedDict, Unpack

class KWArgs(TypedDict):
    name: str
    birth: int

def function(**kwargs: Unpack[KWArgs]):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

function(name='Nao', birth=1980)    # 受け入れられる
function(name='Nao', birth='1980')  # birthがintでないのでエラーとなる
function(name='Nao')                # birthがないのでエラーとなる
function(name='Yamauchi', birth=1980, gender='m')    # genderは指定できない

typing.overrideの導入で、オーバライドの意図がより明確に

 スーパークラスのメソッドをオーバライドする際に、メソッド名を含むシグネチャが間違ったりすることがあります。オーバライドなのか、それとも別のメソッドなのかは型チェックツールには分からないので、バグの要因の一つとなっています。Javaなどの言語ではアノテーションを指定してオーバライドするメソッド定義であることを明示しますが、Python 3.12でデコレータによりそれが可能になりました。

 以下のリストでは、親クラスで定義されているget_distanceメソッドを子クラスでオーバライドしようとしていますが、スペルミスで正しくオーバライドされません。@overrideデコレータを指定することで、型チェックツールがこれを検出できます。

override.py
from typing import override

class Parent:
  def get_distance(self) -> int:
    return 50000

class Child(Parent):
  @override
  def get_destance(self) -> int:    # 型チェックでエラー
    return 100000

次のページ
match文で、switch文に相当する条件分岐の記述が可能に[3.10]

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Pythonの新機能を知ろう!連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook <個人紹介> WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18629 2023/11/14 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング