読者です 読者をやめる 読者になる 読者になる

__getitem__の挙動についてメモ

Python

まず、以下のコードを実行するとどうなるでしょうか。

class Test(object):
    def __getitem__(self, x):
        print 'KEY = [%s]' % x
    
a = Test()
print 'hoge' in a

これが意味のあるコードかどうかは置いといて、この問いに即答できなかったので反省の意味を込めてgetitemの挙動についてメモしておきます。


まず、getitemとはなんぞやというと、

print a['hoge']

というようにアクセスすると、__getitem__(self, x)の第2引数に'hoge'が渡されます。つまり__getitem__が実装されているインスタンスはsubscriptableなオブジェクトになるということです。subscriptableであるということは、a[key]というアクセスができるということです。わかりやすい例だとdict型がsubscriptableですね。(実はbasestringとかtupleとかも…という話は追々。)


また、それとは(多分)独立な概念にiterableという概念があり、これは__iter__(self)関数を実装されているインスタンスはiterableであると言い、

class Test(object):
    def __iter__(self):
        yield 'kobaken'
        yield 'girijin'

a = Test()
for i in a:
    print i

とすると

kobaken
girijin

という出力が得られるということです。listは明らかにiterableですね。


さて、ここで最初に書いたコードに戻ります。実行した結果を想像できた人がどのくらいいるのかわかりませんが、実は実行してみると無限ループになります。そのまま実行すると、出力は

KEY = [0]
KEY = [1]
KEY = [2]
KEY = [3]
...

となります。

'a' in a

を実行すると__getitem__(self, x)の第2引数に 0, 1, 2 ... が渡されて実行されるのかがよくわかりません。


以下のコードを実行すると、

class Test(object):
    pass
a = Test()
print a['hoge']

以下のような例外が送出されます。

TypeError: 'Test' object is unsubscriptable

要はsubscriptableなオブジェクトではないものに対してa[key]というアクセスはできませんよ、ということですね。


また、次のコードを実行すると、

class Test(object):
    pass
a = Test()
print 'hoge' in a

以下のような例外が送出されます。

TypeError: argument of type 'Test' is not iterable

つまりiterableなオブジェクトじゃないと'hoge' in aというアクセスができませんよ、ということなのでしょう。(注: ここ疑問)


そして以下のコードを実行すると、

class Test(object):
    def __getitem__(self, x):
        return 'x is [%s]' % x
a = Test()
print a['hoge']

以下のような出力になります。

x is [hoge]


これでなんとなくsubscriptableとiterableについて一通りわかったようなつもりになったところで、問題を再確認してみます。


疑問 [1]

なぜ__getitem__()を実装してあり__iter__()を実装していないインスタンス

'hoge' in a

というアクセスに対して

TypeError: argument of type 'Test' is not iterable

というエラーを送出しないのか。


疑問 [2]

なぜ__getitem__を実装したインスタンスでは'hoge' in aのアクセスによって

__getitem__(self, 0)
__getitem__(self, 1)
__getitem__(self, 2)
...

が呼ばれるのか?


これらを正しく説明するためにはin演算子が内部でどのように動作しているかを理解しなければならないような気がしますが、それを理解するためにはこの週末では時間が足りなかったようです。また別のエントリに書きたいと思います。



追記(2010/08/29 22:37)
とりあえずここ読めば少し理解が深まりそうですねー。
http://www.python.jp/doc/2.5/ref/sequence-types.html