SHOEISHA iD

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

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

Pythonの新機能を知ろう!

【Python 3.12への道のり】3.10で実装された型機能など、魅力的な機能を正式リリースまでに使いこなす

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

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

パラメータ仕様変数により関数の引数を参照した型定義が可能に

 パラメータ仕様変数とは、引数仕様変数ともいい、関数の引数の仕様を収納した変数のことをいいます。デコレータを使った関数呼び出しの型チェックを厳密化するときなどに使用しますが、これだけではわかりにくいと思うので、まずはデコレータ関数のサンプルを紹介し、そこで起きる問題を見ながら理解していきましょう。

デコレータ関数とは?

 デコレータとは、クラスや関数の前後に実行する処理を追加できる機能です。デコレータ関数は、その処理内容を記述した関数であり、その関数をクラスや関数の定義の直前に「@関数名」と記述することで呼び出すことができます。以下のリストは、デコレータの定義例です。

リスト decolator.py
from typing import Callable, TypeVar

# 戻り値の型を表す型変数の定義
R = TypeVar("R")	(1)

# デコレータ関数loggingの定義
def logging(f: Callable[..., R]) -> Callable[..., R]:		(2)
  def wrapper(*args: object, **kwargs: object) -> R:		(3)
    print(f'{f.__name__}の実行前')
    val = f(*args, **kwargs)
    print(f'{f.__name__}の実行後')
    return val
  return wrapper						(4)

# デコレータを使う関数の定義
@logging
def simple_function(x: str, y: int) -> int:
  print(f'{x}, {y}')
  return int(x) + y

# デコレータ関数の呼び出し
simple_function("200", 100)					(5)

 (1)は型変数Rを定義しています。これはデコレータ関数の引数と戻り値に指定されるCallableオブジェクトの戻り値の型を指定するための型ヒントになります。

 (2)の関数loggingは、簡易なロガーであり、デコレート対象の関数の呼び出しに伴い、呼び出されます。引数と戻り値はCallableオブジェクトであり、これはすなわちデコレート対象の関数とデコレート後の関数そのものとなります。ここで指定するCallableオブジェクトは、引数は任意(...はEllipsisオブジェクトで、省略を意味します)、戻り値はRとなっていることに注意してください。

 (3)で定義している関数wrapperは、デコレータ関数loggingが返す関数となります。wrapper関数には、デコレート対象の関数の引数が渡されます。この引数を用いて、logging関数に渡された関数を呼び出します。関数の呼び出し前後には、print文で実行前後のメッセージを出力しています。

 (4)のreturn文はlogging関数の戻り値であり、(3)で定義した関数です。

 (5)の関数呼び出しでは、デコレータ関数loggingが「simple_function("200", 100)」を引数として呼び出され、ラップされた関数が返されることで、その関数が実行されます。

 実行結果は、以下のようになります。本来のsimple_function関数による出力が、デコレータ関数による出力に挟まれていることが分かります。

simple_functionの実行前
200, 100
simple_functionの実行後

  なお、(5)を以下のように書き換えて実行すると、関数の呼び出しがエラーとなります。それは、第1引数が整数であるので、int(x)がエラーとなるためです。しかし、mypyによる静的な型チェックでは検出できません。それは、(5)の関数呼び出しは実際にはデコレータによって返された関数の呼び出しであり、その関数である(2)のCallableオブジェクトの引数は、何でもよい(...)ということになっているからです。

simple_function(200, "100")	# 実行時エラー

パラメータ仕様変数の追加

 このような問題を受けて、Python 3.10ではパラメータ仕様変数(ParamSpec)が導入されました。上記のリストをパラメータ仕様変数を使って書き換えたのが以下のリストです。

リスト paramspec.py
from typing import Callable, TypeVar, ParamSpec		(1)

# 戻り値の型変数とパラメータ仕様変数の定義
R = TypeVar("R")
P = ParamSpec("P")		(2)

# デコレータ関数loggingの定義
def logging(f: Callable[P, R]) -> Callable[P, R]:	(3)
  def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:		(4)
    print(f'{f.__name__}の実行前')
    val = f(*args, **kwargs)
    print(f'{f.__name__}の実行後')
    return val
  return wrapper
…略…
# デコレータ関数の呼び出し
simple_function("200", 100)
simple_function(200, "100")		(5)

 (1)でParamSpecを参照できるようにimport文を拡張しています。

 (2)で定義しているPが、パラメータ仕様変数です。TypeVarと同様にPsramSpecを使って定義します。

 (3)ではlogging関数を定義していますが、引数のCallableオブジェクトの引数がパラメータ仕様変数Pとなっていることに注目です。これにより、Pを使った型ヒントが可能になっています。

 (4)のwrapper関数も、引数の型ヒントがP.args、P.kwargsと、それぞれパラメータ仕様変数から得られる型ヒントになることにも注目です。

 このリストをmypyに与えてみると、以下のような結果になります。引数に互換性のない(5)の呼び出しでは、arg-typeのエラーになることが分かります。

% mypy paramspec.py 
paramspec.py:24: error: Argument 1 to "simple_function" has incompatible type "int"; expected "str"  [arg-type]
paramspec.py:24: error: Argument 2 to "simple_function" has incompatible type "str"; expected "int"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

  このようにデコレータを使う関数の引数に対する型ヒントを厳密化でき、実行するまでもなく静的型チェックで型エラーを検出することができるようになります。

次のページ
TypeAliasにより明示的な型エイリアスの指定が可能に

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

  • 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/18356 2023/10/02 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング