2008年9月15日月曜日

Python で operator モジュールを使い、組込みの演算子をオブジェクトに対して適用する

1. 関数 reduce と sum を使い合計を求める

Python の組み込み関数 reduce を使うと、リスト要素の合計を求めることができる。

a = [1, 5, 8, 12]
print reduce(lambda x,y: x+y, a)

合計を求めるには、もっと簡単な方法として、組み込み関数 sum がある。

2.1 組み込み関数 によると、

sum( sequence[, start])

startsequence の要素を左から右へ加算してゆき、総和を返します。start はデフォルトで 0 です。 sequence の要素は通常は数値で、文字列であってはなりません。文字列からなるシーケンスを結合する高速かつ正しい方法は ''.join(sequence) です。 sum(range(n), m)reduce(operator.add, range(n), m) と同等です。 バージョン 2.3 で 新たに追加 された仕様です。

実は、sum 関数があることを忘れて、reduce 関数で合計を求めていたことがある。 ^^;

で一度動作を確認しているはずなのになぁ…。

 

2. 加算の演算子を表す operator.add

上記で引用した、組み込み関数 sum の説明には、sum を実装するために reduce を利用して、

reduce(operator.add, range(n), m)

と記述されている。

operator.add

とは、何のことだろう?

6.7 operator -- 関数形式の標準演算子 によると、

operator モジュールは、Python 固有の各演算子に対応している C 言語で実装された関数セットを提供します。例えば、 operator.add(x, y) は式 x+y と等価です。関数名は特殊なクラスメソッドとして扱われます; (...)

これらの関数はそれぞれ、オブジェクトの比較、論理演算、数学演算、シーケンス操作、および抽象型テストに分類されます。

つまり、Python では、Haskell のように関数の引数として、組み込みの演算子

(+)

を渡すことができない。その代わり、operator モジュールの operator.add で代用する。

 

3. operator モジュールのメソッドをクラスで定義する

operator モジュールにあるメソッドをクラスに定義すると、オブジェクトに対して、組み込みの演算子を用いることができる。

例えば、

 __add__(a, b) メソッド

をクラスに定義すると、オブジェクトに対して + 演算子を適用できる。

例えば、

「太郎、21歳」と「花子、15 歳」を足した結果、二人の年齢を足した「名無しの Person オブジェクト」を返す

という仕様を想定する。 「Python のイテレータ」 で定義した Person クラスを使い、試してみる。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return self.name + " " + str(self.age)

    def __cmp__(self, other):
        result = cmp(self.age, other.age)
        if result == 0:
            return cmp(self.name, other.name)
        else:
            return result

    def __add__(a, b):
        return Person("", a.age + b.age)
    
persons = [Person("Tarou", 21), Person("Hanako", 15), Person("Jiro", 15)]

total = Person("", 0)
for person in persons:
    total += person
print total.age

print reduce(lambda x,y: x+y, persons).age

# sum における初期値を設定する。初期値は数値の 0 であることに注意。
print sum(persons, Person("", 0)).age

結果は、

51
51
51

 

4. __XXXXXX_ 系のメソッドはどこに宣言してある?

上記のような関数を見ると、組み込み関数についてしっかりと押さえ、

__XXXXX__ 系

の特殊なメソッドを覚える必要があると感じる。

標準型の階層を手がかりに、Python の中に入っていけるようにならないとダメかなぁ。 (+_+)

それにしても、__XXXXX__ は、どこクラスに宣言されているのだろう?