4.4. オブジェクトへの参照を getattr で取得する

すでにあなたは Python の関数はオブジェクトだということは知っています. まだ知らないのは, 実行時までに名前を知ることなく関数への参照を取得できる方法です. それは getattr 関数を使うことです.

Example 4.10. getattr 関数の紹介

>>> li = ["Larry", "Curly"]
>>> li.pop                       1
<built-in method pop of list object at 010DF884>
>>> getattr(li, "pop")           2
<built-in method pop of list object at 010DF884>
>>> getattr(li, "append")("Moe") 3
>>> li
["Larry", "Curly", "Moe"]
>>> getattr({}, "clear")         4
<built-in method clear of dictionary object at 00F113D4>
>>> getattr((), "pop")           5
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'pop'
1 リストの pop メソッドへの参照を取得します. これは pop メソッドの呼び出しではないことに注意してください. そっちは li.pop() です. これはメソッドそのものです.
2 これも pop メソッドへの参照を返しますが, 今回はメソッドの名前は予め分かっていて, getattr 関数に文字列引数として渡されています. getattr は非常に有用な組み込み関数で, 任意のオブジェクトの任意の属性を返します. この例では, オブジェクトはリストで属性は pop メソッドです.
3 ここまでの「非常に有用」程度では驚かないようなら, これをやってみてください. getattr の返り値メソッドで, あたかも li.append("Moe") と直接呼ぶかのように呼び出せます. しかし関数を直接呼び出してはおらず, 代わりに名前という文字列で関数を特定しています.
4 getattr 関数は辞書にも使えます.
5 理論的には, getattr 関数はタプルにも使えますが, タプルはメソッドを持たないので, どんな属性名を与えても getattr は例外を送出します.

4.4.1. モジュールに getattr

getattr 関数は組み込みのデータ型のためだけのものではありません. モジュールにも使えます.

Example 4.11. apihelper.py での getattr 関数

>>> import odbchelper
>>> odbchelper.buildConnectionString             1
<function buildConnectionString at 00D18DD4>
>>> getattr(odbchelper, "buildConnectionString") 2
<function buildConnectionString at 00D18DD4>
>>> object = odbchelper
>>> method = "buildConnectionString"
>>> getattr(object, method)                      3
<function buildConnectionString at 00D18DD4>
>>> type(getattr(object, method))                4
<type 'function'>
>>> import types
>>> type(getattr(object, method)) == types.FunctionType
True
>>> callable(getattr(object, method))            5
True
1 これは, Chapter 2, 初めての Python プログラムで学んだ odbchelper モジュールの buildConnectionString 関数への参照を返します. (ここで表示されている16進アドレスは私 (筆者 Mr. Mark Pilgrim) のマシンでの値です. あなたのマシンでの出力とは異なります.)
2 getattr を使うことで, 同じ関数への同じ参照が得られます. 一般的に, getattr(object, "attribute")object.attribute と同等です. object にモジュールが来る場合は, attribute には, 関数やクラスやグローバル変数などのモジュールで定義されているものが入ります.
3 そしてこれが実際に info 関数の中で使っている形式です. object は引数として関数に渡され, method はメソッド名や関数名の文字列が渡されます.
4 この例では method は関数名で, type 関数を使うことでそれを確認できます.
5 method は関数なので, 呼び出し可能です.

4.4.2. ディスパッチャとしての getattr

getattr はよくディスパッチャとして使われます. (訳注. 「ディスパッチャ」は「何か関数を呼び出すもの」くらいの意味です.) 例えば, 様々な異なった形式のデータを出力するプログラムを扱っている場合に, 各々の出力形式に対して個別の関数を定義し, 1つの呼び出し関数からデータ形式に合った関数を呼び出せます.

例えば, Web サイトの統計情報を HTML, XML, テキストの 3 つの形式で表示するプログラムを思い浮かべてみましょう. 出力形式の選択はコマンドラインや設定ファイルで指定できるとします. statsout モジュールには output_html, output_xml, output_text の 3 つの関数が定義されているとします. すると, 以下のように 1 つの出力関数でメインのプログラムが書けます.

Example 4.12. getattr でディスパッチャを作る


import statsout

def output(data, format="text"):                              1
    output_function = getattr(statsout, "output_%s" % format) 2
    return output_function(data)                              3
1 output 関数は, 必須の引数 data とオプションの引数 format の 2 つの引数を取ります. format が指定されなかった場合には, デフォルトの text が使われ, テキスト出力の関数が呼ばれることになります.
2 format 引数と "output_" を連結し関数名を作り, statsout モジュールからその関数を取得してきます. こうすると, 後でこのプログラムを他の出力形式もサポートするように拡張するときに, 呼び出し関数を修正しなくて済みます. 例えば output_pdf という名前の関数を statsout モジュールに追加し, output 関数に format 引数として "pdf" を渡すだけです.
3 後は他の関数と同じように出力関数を呼び出すだけです. output_function 変数は実際の statsout モジュールの適切な関数への参照になっています.

ところで, 前の例にバグがあるのが分かりましたか? かなりいい加減な文字列と関数の対応付けをしていて, エラーチェックは行っていません. statsout モジュールに関数が定義されていないフォーマットをユーザが指定した場合は何が起きるでしょうか? おそらく, getattr 関数は None を返し, それが正しい関数の代わりに output_function に割り当てられ, 関数を呼び出している次の行で呼び出しに失敗し例外を送出します. これは良くありません.

幸運なことに, getattr 関数は 3 つ目のオプションの引数にデフォルトの返り値を設定できます.

Example 4.13. getattr のデフォルト値


import statsout

def output(data, format="text"):
    output_function = getattr(statsout, "output_%s" % format, statsout.output_text)
    return output_function(data) 1
1 getattr 関数の 3 つ目の引数を追加したので, この関数呼び出しはきちんと動くことが保証されます. 3 つ目の引数は, 2 つ目の引数で指定した属性やメソッドが見付からなかった場合に返されるデフォルト値です.

ここで見たように, getattr 関数はかなり強力です. この関数は内省の真髄であり, さらに強力な例について後の章で見れます.