演算子のオーバーロードから。
演算子のオーバーロードを行うには、演算子に対応付けられたメソッドを対象クラスに実装してやる必要がある。
以下、演算子と演算子に対応付けられたメソッドの一部。
演算子・処理 | 対応するメソッド名 | メソッド起動の例 |
+ | __add__ | A + B |
- | __sub__ | A - B |
/ | __div__ | A / B |
* | __mul__ | A * B |
| | __or__ | A | B |
インスタンスの作成 | __init__ | Class() |
ガーベージコレクション | __del__ | |
出力・型変換 | __repr__,__str__ | print X, `X`, str(X) |
インスタンスの呼び出し | __call__ | X() |
.を使用した属性へのアクセス | __getattr__ | X.abc |
属性への値の代入 | __setattr__ | X.abc = value |
インデクシング | __getitem__ | X[key] |
シーケンスの特定の要素への代入 | __setitem__ | X[key] = value |
シーケンスの長さ取得 | __len__ | len(X) |
比較 | __cmp__ | X == Y, X < Y |
小なり | __lt__ | X < Y(__cmp__でも対応可) |
同等か | __eq__ | X == Y(__cmp__でも対応可) |
インスタンスが右側に使われた場合の+演算子 | __radd__ | Noninstance + X |
上書き加算 | __iadd__ | X += Y(__add__でも対応可) |
ループ、反復 | __iter__ | forループ, X in Y |
演算子が呼ばれると、対応する__XXX__といった前後にアンダースコアがつけられた関数が呼ばれる。また、そういったメソッドを他の通常のメソッドと区別して「フックメソッド」と呼ばれる。
__getitem__
__getitem__はインデクシングの処理に対応する「フックメソッド」。
対象クラスに定義されているか、対象クラスの継承ツリーに定義されていないとコールされない。
以下、__getitem__の例
>>> class C1:
... def __getitem__(self, index):
... return index * 100
...
>>> a = C1()
>>> a[0]
0
>>> a[1]
100
>>> a[2]
200
>>> a[3]
300
>>>
この__getitem__メソッドはループにも対応可。
>>> for b in a:
... if b > 1000: break
... print b
...
0
100
200
300
400
500
600
700
800
900
1000
>>>
このようにループでも__getitem__が呼ばれる。が、Pythonの中では__getitem__メソッドを実行する
前に、__iter__メソッドが実装されていないか調べている。もし、__iter__があった場合で、ループ処理
の場合には__iter__がコールされる。
__iter__
__iter__は「イテレータプロトコル」をサポートしたオブジェクト(イテレータオブジェクト)を返す。
以下、その例。
>>> class C1:
... def __init__(self, start, stop):
... self.value = start - 1
... self.stop = stop
... def __iter__(self):
... return self
... def next(self):
... if self.value == self.stop:
... raise StopIteration
... self.value += 1
... return self.value ** 2
...
>>> for aa in C1(1,10):
... print aa
...
1
4
9
16
25
36
49
64
81
100
>>>
上記の例では__iter__はselfを返している。
__iter__はイテレータオブジェクトを返す必要があるので、self自身にnextメソッドを持たせる必要がある。
nextメソッドでは現在保持している値に1を加算し、その加算結果を2乗している。
__iter__はループ処理自体を複数回繰り返すことには対応していない。
>>> aa = C1(1,5)
>>> for bb in aa:
... print bb
...
1
4
9
16
25
>>> for bb in aa:
... print bb
...
>>>
複数回実行するにはもう一度オブジェクトを作成しなければならない。
__getattr__
通常の「インスタンスの属性へのアクセス」では呼ばれないとのこと。
>>> class C1:
... def __getattr__(self, name):
... print "name=[", name , "]"
... return self.X
... X = [1,2,3,4]
...
>>> a = C1()
>>> a.X
[1, 2, 3, 4]
>>> a.Y
name=[ Y ]
[1, 2, 3, 4]
>>>
ほんとだ。
XはC1クラスに存在するので、__getattr__はコールされない。
オブジェクトツリーも含めて存在しない場合にのみコールされるとのこと。
上記Yはどこにも存在しないので、__getattr__が呼ばれている。
__setattr__
<オブジェクト>.属性 = 値
といった形の場合、コールされるメソッド。
実際には、
<オブジェクト>.__setattr__(属性名、値)
というコードが実行される。
このメソッドで注意しなければいけないところは、
__setattr__メソッド内で属性へ代入すると無限ループになってしまう、という点。
つまり、
def __setattr__(self, attr, value):
self.X = value
とすると、__setattr__メソッドの中でさらに__setattr__が呼ばれる。
いつまでたっても終了しない。
そこで、こういった場合、__dict__を使用するとのこと。
>>> class C1:
... def __setattr__(self, attr, value):
... if attr == 'age':
... self.__dict__[attr] = value
... else:
... raise AttributeError, attr + ' not allowed'
...
>>> a = C1()
>>> a.age = 1000
>>> a.age
1000
>>> a.ageage = 1000
Traceback (most recent call last):
File "", line 1, in ?
File "", line 6, in __setattr__
AttributeError: ageage not allowed
>>>
な感じ。
__repr__、__str__
str()や、repr()で別の動作をさせたいときに実装する。
__call__
インスタンスを関数のように「呼び出し」たときにコールされる。
>>> class C1:
... def __call__(self):
... return str(self)
...
>>> a = C1()
>>> a()
'<__main__.c1>'
>>>
コールバックハンドラなどを作成するときに使える。
__del__
インスタンスが破棄されるときに呼ばれる。
リファレンスがどこにも存在しなくなり、ガーベージコレクタによって破棄されるときにコールされる
メソッド。
おしまい。
.
0 コメント:
コメントを投稿