関数アノテーションとは
関数アノテーションとは関数の引数と戻り値に付加情報をつける機能です。PEP 3107 -- Function Annotationsで定義されていて、Python 2.6へbackportされていないので、利用するにはPython 3.0以降が必要になります。
まずは、インタラクティブシェルで関数アノテーションの例を見てみましょう。
このように、引数のアノテーションは引数の後に `: expression` の形で、戻り値のアノテーションは引数リストの後に `-> expression` の形で定義します。
アノテーションの内容は関数オブジェクトの `__annotations__` というプロパティに保存されます。そして、関数の実行にはまったく影響しません。
今回は、この関数アノテーション(以降アノテーションと呼ぶ)の実用例をいくつか挙げていきます。
アノテーションでドキュメントを書く
Pythonでは一般的に docstring と呼ばれる方法で関数のドキュメントを書きます。引数や戻り値に関する説明も docstring の中に記述していました。たとえば以下のような感じです。
アノテーションは help() や pydoc で表示されるので、Python3.0 からは引数や戻り値に関するドキュメントをアノテーションとして簡潔に記述することもできます。
anntoolsで自動型変換
アノテーションを活用しているライブラリとして、anntoolsを紹介します。
anntools は関数の引数や戻り値が想定している型や値になっているかどうかをチェックしたり、期待している以外の型のオブジェクトが渡されたときに自動で型変換したりしてくれたりします。
Python 2.6以前にも対応しているので、関数アノテーションのある・なしでどう変わるか見ていきましょう。BlogなどのWebアプリケーションで、記事を編集するリクエストを扱う関数を想定してみます。
リクエストのURLから関数へマッピングするツール(URLマッパー)が自動で型変換してくれない場合は、このように関数内で型チェックや型変換が必要になります。
これを、Python 2.6とanntoolsで書いてみましょう。
確かにうまく動くのですが、元のコードよりも長くなってしまっています。引数の数が増えてくるとanntoolsを使った方が短くなるかもしれませんが、デコレータに対していちいち引数の名前を指定してあげないといけないのが面倒で、あまり便利な気がしません。
今度は Python 3.0 以降で関数アノテーションを使ってみます。
アノテーションがあると型情報を関数に付けることができるので、デコレータに引数を与える必要がありません。引数を二度タイプする手間が省けてすっきりしますし、モジュールやクラス内の関数に対して、自動でデコレータを適用する仕組みも簡単に作れます。
anntoolsの型変換以外の機能として、引数のバリデーションをするサンプルも挙げておきます。
範囲を指定できるなら、境界値テストを自動生成するテストツールなんてものも考えられますね。
アノテーションでoverloading
今度はアノテーションを使ってoverloading(多重定義)を実現してみましょう。
overloadingとは静的型付け言語では一般的な機能で、名前は同じだけど引数の型が異なる関数を別々に定義しておき、関数を呼び出すときには引数の型を元に、どの関数を使うかを決定するものです。
たとえばJavaのPrintStreamクラスでは println()メソッドをオーバーロードしていて、次のように使うことができます。
もちろんPythonのprint関数(Python 3以前はprint文)も、いろいろな型のオブジェクトをうまく扱っているのですが、自分で渡された型に応じて動作を変える関数を作ろうとすると、自分で型チェックを行い分岐しなければなりませんでした。
このようにisinstance()の列を書いていると、overloadingのある静的片付け言語の方が楽かも……なんて思うこともあります。この手間を省く動的overloadingをGuido氏が実装したものが、PythonのSubversionリポジトリの中にあります。
この中のprettyprinter.pyというサンプルで、overloadしている場所はこうなっています。
先ほどのPython 2.6+anntoolsと同じで、デコレータに型を指定しています。これをアノテーションを使うように書き換えてみました。
アノテーションで型指定していない引数は自動でobject型として扱うようにしたので、selfの型指定が不要になりました。こちらの方が引数の順番を変えるのも楽そうです。
おわりに
いくつかのサンプルを挙げてみたのですが、デコレータだけでは記述が面倒だった関数の引数や戻り値に対する情報の紐づけが、アノテーションを使うことでスマートにできるようになるのを実感できたでしょうか?
Python 2.6ではアノテーションが使えないこともあり、アノテーションを使ったツールやフレームワークはまだあまりないのですが、Python 3.xが主流になるころには、いろいろなフレームワークでアノテーションを使ったスマートな機能が実現されていることでしょう。