Python Magic Method

 日本の書籍ではクラスの特殊メゾットと呼ばれているメゾットの説明です。英語の書籍でこの表現を見つけて、こちらの方がしっくりくると思ったので、題名だけ英語にしてみました。本文中では"特殊メゾット"で説明します。

 特殊メゾットとは

 例えば、インタラクティブシェルを使って文字列とリストを作り、dir関数で見てください。


>>> s = ""
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__radd__', '__reduce__', '__reduce_ex__','__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'islower', 'isnumeric', 'isspace', 'istitle', 'isunicode', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title','translate', 'upper', 'zfill']
>>> l=[]
>>> dir(l)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__','__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']


 dir関数でオブジェクトのアトリビュートが表示されることを説明しました。
またクラスのカプセル化で最初にアンダースコアが2つ並べられていると重要なアトリビュートであることを説明しました。
 文字列とリストのアトリビュートを比較してみてください。アンダースコアの付いてないメゾットは違うものが多いですが、アンダースコアの付いているメゾットってほとんど同じだということに気づきました?これが今回の主役"特殊メゾット"です。

 アンダースコアが付いていないものは、プログラマがプログラムを打つときに使うメゾットですが、アンダースコアが付いた特殊メゾットは"python"がオブジェクトを扱う標準プロセスで使用することを目的に作られたものです。

 例えば、print命令を文字列に対して行われた場合、文字列の'__str__'関数が呼ばれます。同様にリストのprint命令を実行した場合、リストの'__str__'関数が呼ばれます。つまりprint命令はオブジェクトの'__str__'関数を実行して帰ってきた情報を表示しています。逆にオブジェクトのprint命令に対する動作を変更したければ、'__str__'関数を書き換えれば良いことに気づくと思います。これを演算子のオーバーライドと呼びます。

 この考え方は当然他のプログラム言語にもあるのですが、Pythonの場合言語の設計段階からこの考えを取り入れていることがすごいことだなあと思い、そのメゾットを書き換えるだけでオブジェクトの振る舞い大きく変えることができることから"魔法のようなメゾット"という表現にぴったりだなと思っています。

 演算子のオーバーライドをやってみよう

 ではいつも通り試してみましょう。クラスは独自にベクトル(Vector)クラスというものを作ります。ベクトルの和と差、あと演算記号がないので"*","%"を使って内積、外積の絶対値を求められるクラスを作ってみます。

#coding: shift-jis

class Vector(object):
  def __init__(self, x = 0,y = 0):
    self.x = x
    self.y = y

  def __add__(self, other): ←"+"演算子のオーバーライド
    return Vector(self.x + other.x, self.y + other.y)

  def __sub__(self,other): ←"−"演算子のオーバーライド
    return Vector(self.x - other.x, self.y - other.y)

  def __mul__(self,other): ←"*"演算子のオーバーライド
    return self.x * other.x + self.y *other.y

  def __mod__(self,other): ←"%"演算子のオーバーライド
    return abs(self.x * other.y - self.y * other.x)

  def __abs__(self): ←abs関数の出力を指定します。
    return (self.x **2 +self.y**2) ** 0.5

  def __str__(self): ←print命令文で使います。
    return "Vector(%s,%s)" % (self.x, self.y)

if __name__ == '__main__':
  '''Vector クラスのテスト'''
  a = Vector(5, 12)
  b = Vector(3, 4)
  print "ベクトルa",a
  print "ベクトルb",b
  print "ベクトルの和:a + b=", a+b
  print "ベクトルの差:a - b=", a-b
  print "ベクトルの内積:a * b=", a*b
  print "ベクトルの外積の絶対値:a % b=", a%b
  print "ベクトルの絶対値:|a|=",abs(a)
  print "ベクトルの絶対値:|b|=",abs(b)

raw_input()

出力画面
 ベクトルa Vector(5,12)
 ベクトルb Vector(3,4)
 ベクトルの和:a + b= Vector(8,16)
 ベクトルの差:a - b= Vector(2,8)
 ベクトルの内積:a * b= 63
 ベクトルの外積の絶対値:a % b= 16
 ベクトルの絶対値:|a|= 13.0
 ベクトルの絶対値:|b|= 5.0
 ベクトルは高校生で習います。内積/外積という言葉は大学生にならないと教わりません。難しい言葉は置いといて、"演算子のオーバーライド"っていうのは足し算や引き算なんかの決まりを自分で決められるってことだとわかってもらえれば、十分です。

 他にもいろいろある特殊メゾット

 Pythonにはいっぱい、特殊メゾットがあります。その一部を表にしてみました。

メゾット 説明
オブジェクトの作成/削除時に使用するもの
__init__(self [,args]) 初期化するとき使用されます。
__del__(self) del(self)
算術演算子
__add__(self, other) self + other
__sub__(self, other) self - other
__mul__(self, other) self * other
__div__(self, other) self / other
__iadd__(self, other) self += other
__isub__(self, other) self -= other
__imul__(self, other) self *= other
__idiv__(self, other) self /= other
__pow__(self, other [,modulo]) self ** other, pow(self, other, modulo)
__and__(self, other) self & other
__or__(self, other) self | other
__neg__(self) -self
__pos__(self) +self
組み込み関数で使用するもの
__abs__(self) abs(self)
__divmod__(self, other) divmod(self, other)
__int__(self) int(self)
__long__(self) long(self)
__float__(self) float(self)
__repr__(self) repr(self)
__str__(self) str(self)
比較演算子
__cmp__(self, other) 二つのオブジェクトを比較して、負、0、正を返す
__eq__(self, other) self == other
__ne__(self, other) self != other
__lt__(self, other) self < other
__le__(self, other) self <= other
__gt__(self, other) self > other
__ge__(self, other) self >= other

*比較演算子について
 __cmp__()は"self < other"のとき負の数値、"self > other"のとき正の数値、"self == other"のとき"0"を返す関数でなければいけません。また他の比較演算子の特殊メゾットが定義されている場合には、このメゾットは呼び出されません。