CodeGym /Javaコース /Python SELF JA /オペレーターのオーバーロード

オペレーターのオーバーロード

Python SELF JA
レベル 20 , レッスン 0
使用可能

6.1 マジックメソッド

Pythonにおけるオペレーターのオーバーロードは、組み込みオペレーター(例えば、+-*/)の振る舞いをカスタムクラスで定義または変更することを可能にするんだよ。 これは特別なメソッド、つまりマジックメソッドを使って行うんだ。

例えば、自分のクラスで比較オペレーターをオーバーロードすることができるよ:

オペレーター アンダースコアなしメソッド メソッドシグネチャ
== eq() __eq__(self, other)
!= ne() __ne__(self, other)
< lt() __lt__(self, other)
<= le() __le__(self, other)
> gt() __gt__(self, other)
>= ge() __ge__(self, other)

例えば、自分のクラスを書いて、クラスのオブジェクトが比較されたときに自分のやり方で動作してほしいとするよね。その場合、クラスで«__eq__»メソッドを実装すれば、Pythonはコードでクラスのオブジェクトが比較されるたびにこのメソッドを呼び出してくれるよ。

例:


class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
        
# 使用方法
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2)  # 出力: True
print(v1 == v3)  # 出力: False

毎回、2つのオブジェクトを比較すると、Pythonは«__eq__»関数が実装されているか確認するんだ。それがあれば、Pythonはそれを呼び出すよ。そして、実装されていない場合は、オブジェクトへの参照を比較するだけになるんだ。

実際には上記の例では(メソッドの存在を確認する必要があるだけで)次のように書かれているんだ:


# 使用方法
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2))  # 出力: True
print(v1.__eq__(v3))  # 出力: False

6.2 すべてのオペレーターの一覧

オーバーロード可能なオペレーターのグループは全部で6つあるよ。

算術オペレーター:

オペレーター アンダースコアなしメソッド メソッドシグネチャ
+ add __add__(self, other)
- sub __sub__(self, other)
* mul __mul__(self, other)
/ truediv __truediv__(self, other)
// floordiv __floordiv__(self, other)
% mod __mod__(self, other)
** pow __pow__(self, other)

比較オペレーター:

オペレーター アンダースコアなしメソッド メソッドシグネチャ
== eq __eq__(self, other)
!= ne __ne__(self, other)
< lt __lt__(self, other)
<= le __le__(self, other)
> gt __gt__(self, other)
>= ge __ge__(self, other)

論理オペレーター:

オペレーター アンダースコアなしメソッド メソッドシグネチャ
& and __and__(self, other)
| or __or__(self, other)
^ xor __xor__(self, other)
~ invert __invert__(self)

インデックスとスライスオペレーター:

オペレーター メソッド
obj[key] __getitem__(self, key)
obj[key] = value __setitem__(self, key, value)
del obj[key] __delitem__(self, key)

単項オペレーター:

オペレーター メソッド
- __neg__(self)
+ __pos__(self)
abs() __abs__(self)
~ __invert__(self)

代入オペレーター:

オペレーター メソッド
+= __iadd__(self, other)
-= __isub__(self, other)
*= __imul__(self, other)
/= __itruediv__(self, other)
//= __ifloordiv__(self, other)
%= __imod__(self, other)
**= __ipow__(self, other)

こうしたことから、Pythonが遅いのはこのせいかもね。オペレーターが実行されるたびに、クラスや親クラスの同等の関数を探しに行くんだ。それでも、世界で最もコンパクトなコードを書くことができるんだよ :)

6.3 インデックスオペレーター

オブジェクトを比較したり集合を引いたりできるのは、ある意味当然のことだよね。そして、ロジックや数学的な操作が想定されるクラスを書くときにそれに気づくはず。

こんな面白い例をやってみたいんだけど、インデックスオペレーターについてです。さっそくコードから始めましょう:


class CustomList:
    def __init__(self, data):
        self.data = data
        
    def __getitem__(self, index):
        return self.data[index]
        
    def __setitem__(self, index, value):
        self.data[index] = value
        
    def __delitem__(self, index):
        del self.data[index]
        
    def __repr__(self):
        return repr(self.data)
        
# 使用方法
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1])  # 出力: 2
c_list[1] = 10
print(c_list)  # 出力: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list)  # 出力: [1, 3, 4, 5]

ここで、3つの操作の例が見られるよ:

  • インデックスを使ったデータの読み込み
  • インデックスを使ったデータの書き込み
  • インデックスを使ったデータの削除さえも。

データがリストとして保存されているとは限らないんだよ。また、インデックスが数値である必要もないんだ。例えば、クラスdictionary(辞書)はまさにそのように実装されているんだ。

SuperListを作成しよう

listクラスを覚えている?それに要素を代入できるけど、すでに存在するインデックスに対してだけだよね。では、独自のクラスを作って、SuperListと名付けよう。その要素にはどんなインデックスでもアクセスできるようにしよう:

  • インデックスが< 0の場合、要素を先頭に挿入する
  • インデックスが>= lenの場合、要素を末尾に追加する
  • それ以外の場合は単に要素を返す

例:


class SuperList(list):
    def __init__(self, value):
        super().__init__(value)
        
    def __setitem__(self, index, value):
        if index >= len(self):
            super().append(value)
        elif index < 0:
            super().insert(0, value)
        else:
            super().__setitem__(index, value)
        
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst)  # [99, 1, 2, 3, 100]

インデックスのオーバーロードは素晴らしい可能性を秘めているので、ぜひ実際に使ってみてね。今日はここまでです。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION