Python 特殊メソッド

特殊メソッド(Magic Methods)は、Pythonのクラスに特定の機能を追加するために使用される特別なメソッドです。これらのメソッドは、ダンダーメソッド(dunder methods)とも呼ばれ、名前が二つのアンダースコア(__)で囲まれています。特殊メソッドは、クラスのインスタンスが特定の操作を受けたときに自動的に呼び出されます。

以下に、いくつかの主要な特殊メソッドについて解説します。

__init__ メソッド

__init__ メソッドは、クラスのインスタンスが作成されるときに呼び出されるコンストラクタです。

class MyClass:
    def __init__(self, value):
        self.value = value

obj = MyClass(10)
print(obj.value)  # 10

__str__ および __repr__ メソッド

__str__ メソッドは、print() 関数や str() 関数によって呼び出され、オブジェクトの人間に読みやすい文字列表現を返します。__repr__ メソッドは、repr() 関数やデバッグのために呼び出され、オブジェクトの公式な(開発者向けな)文字列表現を返します。

class MyClass:
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return f"MyClass with value {self.value}"

    def __repr__(self):
        return f"MyClass(value={self.value})"

obj = MyClass(10)
print(str(obj))  # MyClass with value 10
print(repr(obj)) # MyClass(value=10)

repr() 関数について詳しく解説します。repr() 関数は、オブジェクトの「公式な」文字列表現を返すための関数です。この文字列表現は、できる限り有用であり、オブジェクトの再作成に使えるべきものとされています。これに対して、str() 関数は人間にとって読みやすい文字列表現を返します。


repr() 関数の概要

目的:オブジェクトの「公式な」(開発者向けな)文字列表現を返す。
使用場面:デバッグやログ記録、シリアライゼーションなど、オブジェクトの詳細な情報が必要な場合に使用されます。
実装方法:クラス内で __repr__ メソッドを定義します。

__repr__ と __str__ の違い

__repr__:repr() 関数やインタプリタでの表示に使用されます。目的は、オブジェクトの詳細で正確な情報を提供し、可能であればオブジェクトの再作成に使える文字列表現を返すことです。
__str__:str() 関数や print() 関数での表示に使用されます。目的は、人間が読みやすい形式でオブジェクトの情報を提供することです。

例を用いた説明

__repr__ メソッドの定義

class MyClass:
    def __init__(self, value):
        self.value = value

    def __repr__(self):
        return f"MyClass(value={self.value})"

    def __str__(self):
        return f"Value: {self.value}"

obj = MyClass(10)
print(repr(obj))  # MyClass(value=10)
print(str(obj))   # Value: 10

この例では、__repr__ メソッドがオブジェクトの公式な文字列表現を返し、__str__ メソッドが人間にとって読みやすい形式の文字列表現を返しています。

repr() の使用例

デバッグでの使用

repr() はデバッグ時に非常に便利です。print() でオブジェクトの詳細情報を表示したい場合に使用されます。

obj = MyClass(20)
print(f"Debug info: {repr(obj)}")  # Debug info: MyClass(value=20)

リスト内のオブジェクト表示

リストや他のコレクション内のオブジェクトを表示するときに repr() を使用すると、各オブジェクトの詳細な情報を得ることができます。

objects = [MyClass(1), MyClass(2), MyClass(3)]
print(objects)  # [MyClass(value=1), MyClass(value=2), MyClass(value=3)]

公式な(開発者向けな)文字列表現を返す理由

公式な文字列表現は、デバッグや開発時に非常に役立ちます。例えば、以下のように、オブジェクトの状態を明確に知ることができ、バグの発見が容易になります。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

p = Point(1, 2)
print(repr(p))  # Point(x=1, y=2)

この場合、Point オブジェクトの状態(x と y の値)が明確に表示されます。

print(path) # /home/sample.txt
print(repr(path)) # PosixPath('/home/sample.txt')

>>
/home/sample.txt
PosixPath('/home/sample.txt')

算術演算子メソッド

これらのメソッドは、算術演算子(+, -, *, / など)をオーバーロードするために使用されます。

• __add__(self, other):+ 演算子
• __sub__(self, other):- 演算子
• __mul__(self, other):* 演算子
• __truediv__(self, other):/ 演算子

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)

    def __str__(self):
        return f"Point({self.x}, {self.y})"

p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = p1 + p2
print(p3)  # Point(4, 6)
__add__を理解するためにpathlibモジュールと同じものを作ってみる
class MyPath:
    def __init__(self, path):
        self.path = path

    def __add__(self, other):
        return MyPath(self.path + '/' + other.path)

    def __str__(self):
        return self.path
    
    def __repr__(self):
        return f'MyPath({self.path})'

mypath = MyPath('/home')
mypath2 = MyPath('sample.txt')

print('自作のMyPathクラス')
print(mypath + mypath2) 

# /home/sample.txt

比較演算子メソッド

これらのメソッドは、比較演算子(==, !=, <, <=, >, >=)をオーバーロードするために使用されます。

• __eq__(self, other):== 演算子
• __ne__(self, other):!= 演算子
• __lt__(self, other):< 演算子
• __le__(self, other):<= 演算子
• __gt__(self, other):> 演算子
• __ge__(self, other):>= 演算子

class Point:
    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

p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(2, 3)
print(p1 == p2)  # True
print(p1 == p3)  # False
  1. プロレスラーが同じ団体に所属していればTrue,違うのであればFlase

# __eq__メソッド プロレスラーが同じ団体に所属していればTrue,違うのであればFlaseを返す

class ProWrestler:
    def __init__(self, name, company):
        self.name = name
        self.company = company

    def __eq__(self, other):
        return self.company == other.company
    
    def __str__(self):
        return f"{self.name}{self.company}所属"
    
    def __repr__(self):
        return f"ProWrestler({self.name}, {self.company})"
    

tanahashi = ProWrestler("Tanahashi", "NJPW")
naito = ProWrestler("Naito", "NJPW")
okada = ProWrestler("Okada", "AEW")
omega = ProWrestler("Omega", "AEW")
kiyomiya = ProWrestler("Kiyomiya", "NOAH")
kenoh = ProWrestler("Kenoh", "NOAH")

print("プロレスラーの所属")
print(tanahashi == naito)  # True
print(okada == omega)  # True
print(kiyomiya == kenoh)  # True
print(tanahashi == okada)  # False
print(tanahashi == kiyomiya)  # False

コンテナ型メソッド

これらのメソッドは、オブジェクトをコンテナ(リスト、タプル、辞書など)として振る舞わせるために使用されます。

• __len__(self):len() 関数
• __getitem__(self, key):インデックスアクセス
• __setitem__(self, key, value):インデックス代入
• __delitem__(self, key):インデックス削除
• __contains__(self, item):in 演算子

class CustomList:
    def __init__(self, elements):
        self.elements = elements

    def __len__(self):
        return len(self.elements)

    def __getitem__(self, index):
        return self.elements[index]

    def __setitem__(self, index, value):
        self.elements[index] = value

    def __delitem__(self, index):
        del self.elements[index]

    def __contains__(self, item):
        return item in self.elements

cl = CustomList([1, 2, 3])
print(len(cl))        # 3
print(cl[1])          # 2
cl[1] = 42
print(cl[1])          # 42
del cl[1]
print(len(cl))        # 2
print(42 in cl)       # False
# __len__メソッド 所属団体クラスを作成して、その団体に所属しているプロレスラーの数を返す

class ProWreslingCompany:
    def __init__(self, company):
        self.company = company
        self.wrestlers = []
    
    def add_wrestler(self, wrestler):
        if not isinstance(wrestler, ProWrestler):
            raise ValueError("プロレスラーのみ追加可能です")
        self.wrestlers.append(wrestler)
    
    def __len__(self):
        return len(self.wrestlers)
    
    
njpw = ProWreslingCompany("NJPW")
njpw.add_wrestler(tanahashi)
njpw.add_wrestler(naito)

print("プロレス団体のプロレスラー数")
print(len(njpw))

イテレーションメソッド

これらのメソッドは、オブジェクトをイテラブルとして振る舞わせるために使用されます。

• __iter__(self):イテレータオブジェクトを返す
• __next__(self):次の要素を返す

class CustomIterator:
    def __init__(self, elements):
        self.elements = elements
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.elements):
            raise StopIteration
        element = self.elements[self.index]
        self.index += 1
        return element

ci = CustomIterator([1, 2, 3])
for element in ci:
    print(element)  # 1, 2, 3

コンテキストマネージャメソッド

これらのメソッドは、with 文を使用する際に、リソースの取得と解放を管理するために使用されます。

• __enter__(self):コンテキストに入るときに呼び出される
• __exit__(self, exc_type, exc_value, traceback):コンテキストを出るときに呼び出される

class CustomContextManager:
    def __enter__(self):
        print("Entering context")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting context")

with CustomContextManager():
    print("Inside context")

まとめ

特殊メソッドは、クラスをより柔軟かつ直感的に扱うための強力なツールです。これらを適切に使用することで、カスタムクラスをPythonの組み込み型のように扱うことができ、より読みやすく、使いやすいコードを作成することができます。

この記事が気に入ったらサポートをしてみませんか?