このBlogは移転しました。今後は aish.dev を御覧ください。

Pythonの特殊メソッド

Python関連のブログやTwitterでの発言を見ていると、ときどき

obj() は obj.__call__() のシンタックスシュガー

とか、

len(obj) は、 obj.__len__() と同じ

というような発言を見かけるとこがある。本当だろうか?

obj() は obj.__call__() のシンタックスシュガー?

もしobj() が obj.__call__() のシンタックスシュガーなら、obj.__call__() は obj.__call__.__call__() のシンタックスシュガーであり、obj.__call__.__call__() は obj.__call__.__call__.__call__() のシンタックスシュガー… という事になってしまい、いつまでたっても処理を実行できなくなってしまう。 という訳で、obj() を obj.__call__() のシンタックスシュガーとは呼ぶことはできないだろう。

len(obj) は obj.__len__() のシンタックスシュガー?

こんな例を考えてみよう。

class MyList(list):
    pass

obj = MyList([1,2,3]))
print(len(obj))

とすれば、objの長さ 3 が正しく出力される。この例をちょっといじって

class MyList(list):
    pass

obj = MyList([1,2,3]))

obj.__len__ = 100

print(len(obj))

としてみよう。オブジェクトに、__len__ という属性値を追加してしまうのだ。もしlen(obj)がobj.__len__()と等価なら、100()という式を評価することになってエラーが発生するだろう。

しかし、実はこの場合でも正しく 3 が出力されてしまう。従って、len(obj)もobj.__len__()のシンタックスシュガーとは呼べないのである。

Pythonの特殊メソッド

ここで使用した__call__()や__len__()はPythonでは特殊メソッドと呼ばれ、C++演算子オーバロードと同じようにオブジェクトで使用する演算子を定義する際に使用される。 ただし、この「特殊メソッド」は クラスから作成されるオブジェクトでのみ使用できるメソッドで、それ以外の組み込み型には同じルールは適用されないのである。

もしobjがクラスから生成したオブジェクトの場合、obj()とすればobj.__call__()が呼び出されるが、それ以外の種類のオブジェクトでは__call__というメソッドはまったく関係がない。len(obj)の場合も同様で、必ずしもオブジェクトの__len__メソッドが関係するわけではないのである。この辺の動作は少々こみ入っており、一般化して「シンタックスシュガーである」と簡単に言い切ることはできない。

通常のPythonプログラミングではこのことを意識する必要はあまりないが、既存のオブジェクトに特殊メソッドを追加して動作をカスタマイズしたり、リストやタプルなどの組み込み型から継承してクラスを定義する場合などはちょっと困ったことになる場合がある。どちらの場合でも、特殊メソッドを使って動作をカスタマイズするならば、対象となるオブジェクトの型について十分な知識が必要なのである。