Pythonでオブジェクト指向プログラミングの学習を進め、class
の定義に慣れてくると、__init__
のようにアンダースコア2つで囲まれた不思議な名前のメソッドを目にする機会が増えます。
これらは「特殊メソッド」と呼ばれ、自作のクラスを、まるでPythonにもともと備わっているリストや辞書のように、直感的で”Pythonらしく”振る舞わせるための鍵となる機能です。
print(my_object)
の結果を、もっと分かりやすい表示にしたい」「自作のオブジェクト同士を
+
演算子で足し算できるようにしたい」こうした要望はすべて、特殊メソッドを正しく実装することで実現可能です。
この記事では、特殊メソッドとは何かという基本的な概念から、使用頻度の高い主要な特殊メソッドの使い方、そしてそれらを活用してクラスを拡張していく実践的な方法まで、具体的なサンプルコードを交えながら徹底的に解説していきます。
【本記事の信頼性】
- 執筆者は元エンジニア
- 大手プログラミングスクールのWebディレクター兼ライターを経験
- 自らも地元密着型のプログラミングスクールを運営
受講生から評判の良いプログラミングスクール
スクール |
特徴 |
受講料金 |
大手比較サイトで4年連続人気NO.1!受講生からの評判も非常に高く、Web系のエンジニアを目指すならRUNTEQ一択。 | 550,000円(給付金適用あり) | |
月単価80万円以上の現役エンジニア講師による指導!一度入会すればサポートは半永久的。 | 498,000円 | |
格安で質の高いWeb制作スキルを習得したい人におすすめ!業界最安級の料金でありながら、コミュニティやサポートが充実。 | 129,800円~ | |
完全無料でプログラミングが学べる貴重なスクール!最短1ヶ月で卒業可能。ゼロスク運営会社への就職もできる。 | 無料 | |
長期間に渡って学習し、希少人材を目指す人に最適なスクール!受講料は高いものの、高収入を得られる人材を目指せる。 | 96~132万円 |
Pythonの特殊メソッド(ダンダーメソッド)とは?
特殊メソッドとは、その名前が前後を二つのアンダースコア(__
)で囲まれているメソッドの総称です。
この見た目から、Double Underscoreの略で「ダンダーメソッド(Dunder Methods)」という愛称で呼ばれることもあります。
特殊メソッドの最も重要な役割は、Pythonの特定の構文や組み込み関数が実行された時に、裏側で自動的に呼び出されるという点にあります。
例えば、+
演算子を使えば数値の足し算ができますし、len()
関数を使えばリストの要素数を取得できます。
私たちが自作したクラスのオブジェクトに対して、このような演算子や組み込み関数が使われた際に、どのような振る舞いをするべきかを定義するのが特殊メソッドなのです。
これにより、自作のクラスがPythonの言語仕様により深く統合され、より直感的で使いやすいものになります。
最も基本的な特殊メソッド:__init__ と __str__
まずは、クラスを定義する上でほぼ必ずと言っていいほど使われる、最も基本的で重要な2つの特殊メソッドから見ていきましょう。
__init__:インスタンスの初期化を行う
__init__
は、クラスからインスタンスが生成される際に、そのインスタンスを初期化するために自動的に呼び出される特殊メソッドです。
このメソッド内で、インスタンスが持つべき属性(インスタンス変数)に初期値を設定します。
サンプルコード
class User:
# インスタンス生成時に自動で呼ばれる
def __init__(self, name, age):
print('__init__が呼び出されました。')
self.name = name
self.age = age
# インスタンスを生成すると、__init__が実行される
user = User('Taro Yamada', 30)
# __init__で設定された属性にアクセスできる
print(f'名前: {user.name}')
print(f'年齢: {user.age}')
実行結果
__init__が呼び出されました。
名前: Taro Yamada
年齢: 30
User(...)
というコードが実行された瞬間に__init__
メソッドが呼び出され、引数として渡された'Taro Yamada'
と30
がインスタンスの属性として設定されていることが分かります。
__str__:print()関数の表示を分かりやすくする
__init__
を実装しただけのクラスのインスタンスをprint()
関数で表示しようとすると、何が表示されるでしょうか。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
user = User('Jiro Suzuki', 25)
print(user)
上記コードを実行すると、以下のようになります。
<__main__.User object at 0x10e34a7d0>
これでは、クラス名とオブジェクトが格納されているメモリアドレスが表示されるだけで、どのようなデータを持っているのか全く分かりません。
この問題を解決するのが__str__
メソッドです。
このメソッドをクラスに定義しておくと、print()
関数やstr()
関数でそのインスタンスが使われた際に自動的に呼び出され、その戻り値の文字列が表示されるようになります。
サンプルコード
class User:
def __init__(self, name, age):
self.name = name
self.age = age
# print()で呼ばれた際の表示内容を定義
def __str__(self):
return f'User(name={self.name}, age={self.age})'
user = User('Jiro Suzuki', 25)
print(user) # __str__が自動的に呼び出される
実行結果
User(name=Jiro Suzuki, age=25)
__str__
を実装したことで、print()
の出力がオブジェクトの内容を表す、人間にとって非常に分かりやすい表現に変わりました。
__str__と__repr__の重要な違い
__str__
と非常によく似た特殊メソッドに__repr__
があります。
この2つは混同されがちですが、明確な役割の違いが存在します。
__str__
(for String): エンドユーザー向けの、非公式で読みやすい文字列を返すことを目的とします。print()
関数などで呼び出されます。__repr__
(for Representation): 開発者向けの、公式で曖昧さのないオブジェクト表現を返すことを目的とします。理想的には、eval(repr(obj)) == obj
が成立するような、そのオブジェクトを再現できる文字列を返すことが推奨されます。インタラクティブシェルで変数名だけを入力してEnterキーを押した場合などに呼び出されます。
もし__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(x={self.x}, y={self.y})'
p = Point(10, 20)
print(p) # __str__が呼ばれる
print(str(p)) # __str__が呼ばれる
print(repr(p)) # __repr__が呼ばれる
実行結果は以下の通りです。
座標は (10, 20) です。
座標は (10, 20) です。
Point(x=10, y=20)
覚えておきたい主要な特殊メソッド
__init__
や__str__
以外にも、Pythonにはクラスの振る舞いを豊かにするための特殊メソッドが数多く用意されています。
ここでは、特によく使われるものをカテゴリ別に紹介します。
算術演算子を定義する特殊メソッド
+
や-
といった算術演算子を、自作のクラスで使えるようにします。
メソッド | 演算子 | 説明 |
---|---|---|
__add__(self, other) |
+ |
加算 |
__sub__(self, other) |
- |
減算 |
__mul__(self, other) |
* |
乗算 |
比較演算子を定義する特殊メソッド
==
や<
といった比較演算子の振る舞いを定義します。
メソッド | 演算子 | 説明 |
---|---|---|
__eq__(self, other) |
== |
等価 |
__lt__(self, other) |
< |
より小さい |
__le__(self, other) |
<= |
以下 |
__gt__(self, other) |
> |
より大きい |
__ge__(self, other) |
>= |
以上 |
コンテナ型を模倣する特殊メソッド
len()
や[]
(添字アクセス)など、リストや辞書のようなコンテナ型の振る舞いをクラスに実装します。
メソッド | 関数/構文 | 説明 |
---|---|---|
__len__(self) |
len() |
要素数を返す |
__getitem__(self, key) |
obj[key] |
指定されたキーの要素を取得する |
__setitem__(self, key, value) |
obj[key] = value |
指定されたキーに要素を設定する |
__delitem__(self, key) |
del obj[key] |
指定されたキーの要素を削除する |
【実践】特殊メソッドでクラスを拡張する
それでは、これらの特殊メソッドを実際に使って、2次元ベクトルを表すVector
クラスを拡張してみましょう。
ここで重要なのは、各特殊メソッドをその本来の目的に沿って使うことです。
例えば、len()
関数はコンテナの「要素数」を返すのが自然な使い方です。
ベクトルの「大きさ(長さ)」は要素数とは異なる概念のため、__len__
で実装するのは適切ではありません。
このような場合は、専用のメソッドやプロパティを定義する方が、コードを読む人にとって意図が明確になります。
import math
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# ユーザー向けの文字列表現
def __str__(self):
return f'Vector({self.x}, {self.y})'
# 開発者向けの公式な表現
def __repr__(self):
return f'Vector(x={self.x}, y={self.y})'
# + 演算子 (v1 + v2) の振る舞いを定義
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
return NotImplemented
# == 演算子 (v1 == v2) の振る舞いを定義
def __eq__(self, other):
if isinstance(other, Vector):
return self.x == other.x and self.y == other.y
return False
# ベクトルの大きさ(ノルム)を返すプロパティ
@property
def magnitude(self):
return math.hypot(self.x, self.y)
# インスタンスの作成
v1 = Vector(2, 3)
v2 = Vector(3, 4)
v3 = Vector(2, 3)
# __add__ が呼ばれる
v_sum = v1 + v2
print(f"{v1} + {v2} = {v_sum}")
# __eq__ が呼ばれる
print(f"{v1} == {v2}: {v1 == v2}")
print(f"{v1} == {v3}: {v1 == v3}")
# magnitude プロパティが呼ばれる
print(f"{v1}の大きさ: {v1.magnitude}")
実行結果は以下の通りです。
Vector(2, 3) + Vector(3, 4) = Vector(5, 7)
Vector(2, 3) == Vector(3, 4): False
Vector(2, 3) == Vector(2, 3): True
Vector(2, 3)の大きさ: 3.605551275463989
v1 + v2
という直感的なコードでベクトルの足し算ができたり、v1 == v3
で内容が同じかどうかを判定できたり、v1.magnitude
でベクトルの大きさを取得できたりと、Vector
クラスがまるでPythonの組み込み型であるかのように振る舞っていることがわかります。
これが特殊メソッドの力です。
まとめ
今回は、Pythonの特殊メソッド(ダンダーメソッド)について、その役割から具体的な使い方までを詳しく解説しました。
なお、Pythonを体系的に学んだり、Pythonのスキルを高めたりするためには、プログラミングスクールを利用するのも有効です。
細かな疑問がすぐに解決するだけでなく、現役エンジニアが「質の高いポートフォリオ」を作成するための手助けをしてくれたり、エンジニア就職・転職のコツを教えてくれたりするなど、様々なメリットがありますので、独学に疲れた方は検討してみてはいかがでしょうか。