Pythonの特殊メソッド(`__init__`、`__str__`など)とは何か

この記事では、Pythonの特殊メソッド(マジックメソッド、ダンダーメソッドとも呼ばれる)について解説します。特殊メソッドは、クラスの挙動をカスタマイズするための強力な機能であり、適切に活用することでコードの可読性や表現力を向上させることができます。

特殊メソッドとは

特殊メソッドは、メソッド名の前後に2つのアンダースコア (__) が付いたメソッドです。これらのメソッドは、特定の演算子や組み込み関数がオブジェクトに対して適用された際に、暗黙的に呼び出されます。例えば、+ 演算子__add__ メソッドを、len() 関数は __len__ メソッドを呼び出します。また、特殊メソッドをオーバーライド(再定義)することで、クラスのインスタンスに対する演算や操作をカスタマイズできます。

主要な特殊メソッド

以下に、主要な特殊メソッドとその役割、使用例を示します。

1. オブジェクトの生成と初期化 (__new__, __init__)

  • __new__(cls, *args, **kwargs): オブジェクトの生成を担当します。通常は object.__new__(cls) を呼び出してオブジェクトを生成し、そのオブジェクトを返します。
  • __init__(self, *args, **kwargs): オブジェクトの初期化を担当します。__new__ で生成されたオブジェクトを受け取り、属性の初期化などを行います。
class Person:
    def __new__(cls, name, age):
        print("__new__が呼ばれました")
        instance = super().__new__(cls) # object.__new__(cls) と同じ
        return instance

    def __init__(self, name, age):
        print("__init__が呼ばれました")
        self.name = name
        self.age = age

person = Person("太郎", 20)
print(person.name) # 出力: 太郎
print(person.age)  # 出力: 20

2. 文字列表現 (__str__, __repr__)

  • __str__(self): str() 関数や print() 関数で呼び出され、オブジェクトの人間にとって読みやすい文字列表現を返します。
  • __repr__(self): repr() 関数で呼び出され、オブジェクトの公式な文字列表現を返します。デバッグ時などに有用です。__str__ が定義されていない場合は、__repr__ が代わりに使われます。
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

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

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

point = Point(1, 2)
print(str(point))  # 出力: (1, 2)
print(repr(point)) # 出力: Point(1, 2)

3. 算術演算 (__add__, __sub__, __mul__, __truediv__ など)

これらのメソッドをオーバーライドすることで、オブジェクト間の算術演算を定義できます。

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

    def __add__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x + other.x, self.y + other.y)
        else:
            raise TypeError("Vector同士の足し算のみ可能です。")

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

v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2
print(v3) # 出力: (4, 6)

4. 比較演算 (__eq__, __ne__, __lt__, __gt__ など)

オブジェクト間の比較を定義します。

class Fraction: # 分数クラス
    def __init__(self, numerator, denominator):
        self.numerator = numerator
        self.denominator = denominator

    def __eq__(self, other):
        if isinstance(other, Fraction):
            return self.numerator * other.denominator == self.denominator * other.numerator
        return False

f1 = Fraction(1, 2)
f2 = Fraction(2, 4)
print(f1 == f2) # 出力: True

5. コンテナ型のエミュレート (__len__, __getitem__, __setitem__, __delitem__ など)

これらのメソッドを実装することで、クラスのインスタンスをリストや辞書のように扱うことができます。

class MyList:
    def __init__(self, data):
        self.data = data

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

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

my_list = MyList([1, 2, 3])
print(len(my_list))    # 出力: 3
print(my_list[1])     # 出力: 2

6. callableオブジェクト (__call__)

このメソッドを実装することで、オブジェクトを関数のように呼び出すことができます。

class Greeter:
    def __init__(self, greeting):
        self.greeting = greeting

    def __call__(self, name):
        return f"{self.greeting}, {name}!"

greet = Greeter("Hello")
print(greet("World")) # 出力: Hello, World!

特殊メソッドのオーバライド

Pythonの特殊メソッドをオーバーライドすることで、クラスの振る舞いをカスタマイズできます。ここでは、具体的なユースケースをコード例とともに紹介します。

1. オブジェクトの初期化 (init)

オブジェクトの生成時に自動的に呼び出される__init__メソッドをオーバーライドすることで、インスタンス変数の初期化などをカスタマイズできます。

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
        self.tricks = []

    def add_trick(self, trick):
        self.tricks.append(trick)

my_dog = Dog("ポチ", "柴犬")
print(my_dog.name)  # 出力: ポチ
print(my_dog.breed)  # 出力: 柴犬
my_dog.add_trick("お手")
print(my_dog.tricks) # 出力: ['お手']

2. 文字列表現 (str, repr)

__str__str()関数やprint()関数でオブジェクトを文字列に変換する際に、__repr__repr()関数でオブジェクトの「公式の」文字列表現を取得する際に呼び出されます。

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

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

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

p = Point(3, 4)
print(str(p))   # 出力: (3, 4)
print(repr(p))  # 出力: Point(3, 4)

3. 比較演算子 (eq, ne, lt, gt, le, ge)

これらの特殊メソッドをオーバーライドすることで、オブジェクト同士の比較方法を定義できます。

class Fraction:
    def __init__(self, numerator, denominator):
        self.numerator = numerator
        self.denominator = denominator

    def __eq__(self, other):
        if isinstance(other, Fraction):
            return self.numerator * other.denominator == self.denominator * other.numerator
        return False

f1 = Fraction(1, 2)
f2 = Fraction(2, 4)
print(f1 == f2)  # 出力: True

4. 数値演算子 (add, sub, mul, truediv など)

これらの特殊メソッドをオーバーライドすることで、オブジェクトに対する算術演算を定義できます。

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

    def __add__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x + other.x, self.y + other.y)
        raise TypeError("Vector同士の加算のみ可能です。")

v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2
print(v3.x, v3.y)  # 出力: 4 6

5. コンテナ型としての振る舞い (len, getitem, setitem, delitem, iter)

これらの特殊メソッドをオーバーライドすることで、オブジェクトをリストや辞書のように扱うことができます。

class WordList:
    def __init__(self, words):
        self.words = words

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

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

    def __iter__(self):
        return iter(self.words)

word_list = WordList(["apple", "banana", "cherry"])
print(word_list[0])  # 出力: apple
print(len(word_list)) # 出力: 3
for word in word_list:
    print(word)
# 出力:
# apple
# banana
# cherry

6. 関数呼び出し (call)

__call__をオーバーライドすることで、オブジェクトを関数のように呼び出すことができます。

class Greeter:
    def __init__(self, greeting):
        self.greeting = greeting

    def __call__(self, name):
        return f"{self.greeting}, {name}!"

greet = Greeter("Hello")
print(greet("World"))  # 出力: Hello, World!

まとめ

この記事で紹介した特殊メソッドは一部であり、他にも様々な特殊メソッドが存在します。これらを適切にオーバーライドすることで、Pythonオブジェクト指向プログラミングをより柔軟に、そして表現力豊かに記述することができます。重要なのは、これらの特殊メソッドはPythonによって暗黙的に呼び出されるという点です。そのため、これらのメソッドを適切に実装することで、自然な構文でオブジェクトを操作できるようになります。

Fluent Python Pythonicな思考とコーディング手法 [ Luciano Ramalho ]