【Python】魔法の杖?特殊メソッド(ダンダーメソッド)の秘密と使い方を徹底解説!

こんにちは。ゆうせいです。

Pythonでクラスを書き始めると、誰もが必ず出会う __init__ というメソッド。名前の両側が2つのアンダースコアで囲まれていて、なんだか不思議な見た目をしていますよね。

これはPythonにおける「特殊メソッド」の一つで、実はあなたの書くクラスに、まるで魔法のような力を与えてくれる、とても重要な仕組みなのです! 🧙‍♂️

今回は、この特殊メソッド(別名: ダンダーメソッド)の謎を解き明かし、あなたの作るオブジェクトをPythonの標準機能と連携させる方法を学んでいきましょう。


特殊メソッドって、一体何者?

特殊メソッドとは、その名の通り、Pythonが「特別な状況」で自動的に呼び出してくれる、予め名前が決められたメソッド群のことです。

__init____len__ のように、名前が __(ダブルアンダースコア)で始まり、__ で終わるのが特徴です。この見た目から、Double UNDERscore を略して「ダンダーメソッド(Dunder Method)」という愛称で呼ばれています。

ここでの最重要ポイントは、特殊メソッドは、私たちが直接呼び出すものではないということです。

例えば、len(my_list) と書くと、Pythonが裏側でこっそり my_list.__len__() を呼び出しています。a + b と書けば a.__add__(b) が呼び出されます。

つまり、特殊メソッドは「Pythonの文法や組み込み関数と、あなたの作ったオブジェクトを繋ぐための、公式の接続ポート」のようなものなのです。このポートをあなたのオブジェクトに実装することで、Pythonの豊かな言語機能にシームレスに対応できるようになります。


まずはこれだけ!初心者が知っておくべき特殊メソッド

ダンダーメソッドはたくさんありますが、一度にすべてを覚える必要はありません。まずは、特に重要でよく使うものをいくつか見ていきましょう!

オブジェクトの誕生を司る: __init__

これはもうおなじみですね。クラスからインスタンスが作られる MyClass() のタイミングで、ただ一度だけ自動的に呼ばれるメソッドです。オブジェクトの初期設定(名前や年齢など、固有のデータを持たせる)を行う、まさに「誕生の儀式」です。

オブジェクトの「顔」を決める: __str____repr__

オブジェクトを print() した時、何が表示されるかを決めるのが __str__ です。これは、ユーザーにとって分かりやすい、非公式な文字列表現を返すのが目的です。

一方、__repr__ は、開発者向けの公式な文字列表現を返します。理想的には、その文字列をPythonコードとして実行すると、元のオブジェクトを復元できるようなものが望ましいとされています。“representation”(=表現)の略です。

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __str__(self):
        # print()で表示される、人間に分かりやすい表現
        return f"『{self.title}』(著者: {self.author})"

    def __repr__(self):
        # 開発者向けの、曖昧さのない表現
        return f"Book(title='{self.title}', author='{self.author}')"

my_book = Book("銀河鉄道の夜", "宮沢賢治")

# __str__が呼ばれる
print(my_book)

# 対話モードなどで my_book とだけ入力すると __repr__が呼ばれる
# Jupyter Notebookなどではこちらが表示されることが多い
my_book


print() の結果は 『銀河鉄道の夜』(著者: 宮沢賢治) となり、my_book とだけ評価した結果は Book(title='銀河鉄道の夜', author='宮沢賢治') となります。この二つを定義しておくと、デバッグが非常に楽になりますよ!

オブジェクトの「長さ」を定義する: __len__

あなたの作ったオブジェクトに対して len() 関数を使えるようにするのが __len__ です。例えば、トランプの「山札」クラスを作ったとしましょう。

class Deck:
    def __init__(self):
        # 簡単のため、カードをただの数字リストで表現
        self.cards = list(range(52))

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

my_deck = Deck()

# __len__が呼ばれる
print(f"山札の残り枚数は {len(my_deck)} 枚です。") # 52

my_deck.get_card_count() のようなメソッドを作るより、len(my_deck) と書ける方がずっと直感的で「Pythonらしい」と思いませんか?

オブジェクトをリストのように扱う: __getitem__

my_list[0] のように、[] を使った要素へのアクセスを可能にするのが __getitem__ です。これを実装すると、オブジェクトがまるでリストや辞書のように振る舞えるようになります。

class Deck:
    def __init__(self):
        self.cards = list(range(52))

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

    def __getitem__(self, position):
        # position番目のカードを返す
        return self.cards[position]

my_deck = Deck()

# __getitem__が呼ばれる
print(f"山札の一番上のカードは {my_deck[0]} です。")
print(f"山札の一番下のカードは {my_deck[-1]} です。")

このメソッド一つで、オブジェクトが急にコレクションらしくなりましたね!


演算子を独自に定義する ➕

特殊メソッドを使えば、+== といった演算子の振る舞いを、あなたのオブジェクトに合わせて独自に定義(オーバーロード)できます。

例えば、2次元ベクトルを表すクラスで、ベクトル同士の足し算を + で実現してみましょう。

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

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

    def __add__(self, other):
        # + 演算子が使われた時に呼ばれる
        new_x = self.x + other.x
        new_y = self.y + other.y
        return Vector(new_x, new_y)

v1 = Vector(2, 3)
v2 = Vector(5, 1)

# v1.__add__(v2) が呼ばれる
v3 = v1 + v2

print(f"{v1} + {v2} = {v3}") # Vector(2, 3) + Vector(5, 1) = Vector(7, 4)

これからの学習に向けて

特殊メソッドは、Pythonという言語の根幹をなす、非常にパワフルな機能です。

いきなり全てを暗記する必要はありません。まずは __init__, __str__, __repr__ を全てのクラスで書く習慣をつけてみましょう。

そして、プログラミングをしていく中で、「このオブジェクトの長さを知りたいな」「このオブジェクト同士を足し算できたら便利だな」と感じた時がチャンスです。その瞬間に、「これに対応するダンダーメソッドはなんだろう?」と調べてみてください。

この探求を繰り返すことで、あなたはPythonの思想をより深く理解し、直感的で美しいコードを書ける真のPythonistaへと成長していくことでしょう。

セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。