閉じる

【Python】特殊メソッド一覧|クラス設計が楽になる実用リファレンス

Pythonの特殊メソッドは、クラスに「Pythonらしいふるまい」を与えるための強力な仕組みです。

演算子オーバーロードやコンテナのような挙動、コンテキストマネージャなど、多くの標準機能は特殊メソッドで支えられています。

本記事では主な特殊メソッドを一覧形式で整理しつつ、実際のクラス設計に役立つ書き方とパターンを詳しく解説します。

Pythonの特殊メソッドとは

特殊メソッド(dunderメソッド)の基本と役割

Pythonの特殊メソッドとは、名前が__xxx__のように両端をアンダースコアで囲まれたメソッドのことです。

ダブルアンダースコアからdunderメソッドとも呼ばれます。

これらは演算子や組み込み関数、構文が実行されたときに自動的に呼び出されるフックとして機能します。

例えば、次のような関係があります。

  • a + ba.__add__(b)
  • len(x)x.__len__()
  • with obj:obj.__enter__()obj.__exit__()

開発者は通常__len____enter__を直接呼び出すことは少なく、Pythonの構文を使うだけで自動的に特殊メソッドが呼ばれるという形になります。

特殊メソッドを使うメリット

特殊メソッドを活用する最大のメリットは、自作クラスを標準ライブラリと同じ感覚で扱えるようになることです。

例えば、数値風のクラスに__add__を実装しておけばa + bという自然な形で扱えますし、コレクション風のクラスに__len____iter__を実装すればlen(x)for item in x:がそのまま使えます。

また__repr____str__を適切に定義することでデバッグ時に読みやすい出力が得られ、__enter____exit__を使うことでリソース管理のミスを減らせます

オブジェクトの生成と文字列表現の特殊メソッド一覧

__new__と__init__

__new____init__は、オブジェクト生成時に使われるペアです。

  • __new__(cls, ...)
    クラスから実際のインスタンスを作るクラスメソッド(実際は静的メソッドとして定義)です。必ずインスタンスを返す必要があります。主にイミュータブルな型(数値、文字列など)を継承するときにカスタマイズします。
  • __init__(self, ...)
    生成されたインスタンスを初期化するためのメソッドです。戻り値は必ずNoneでなければなりません。

__new__と__init__の基本実装例

Python
class MyNumber(int):
    # インスタンス生成前に値を加工したい場合の例
    def __new__(cls, value):
        # ここでは負の値を絶対値に変換してからintを生成する
        obj = super().__new__(cls, abs(value))
        return obj

    def __init__(self, value):
        # __new__で実際に格納された値はすでにabs(value)
        # ここではログ出力や追加属性の設定だけを行う
        self.original_value = value


n = MyNumber(-10)
print(n)                # __str__はintのものが使われる
print(n.original_value)
実行結果
10
-10

__new__を意識すべきケース

通常のクラス設計では__init__だけで十分ですが、イミュータブルな組み込み型を継承するときシングルトンのような生成制御を行うときには__new__を使います。

__del__

__del__(self)は、オブジェクトがガベージコレクションで破棄されるときに呼ばれる「デストラクタ」のようなメソッドです。

ただし呼ばれるタイミングは保証されず、プロセス終了時には呼ばれないこともあるため、重要なリソース解放の手段としては推奨されません

Python
class Tracked:
    def __init__(self, name):
        self.name = name
        print(f"[INIT] {self.name}")

    def __del__(self):
        # ここで重要な処理をしてはいけない(呼ばれない場合がある)
        print(f"[DEL] {self.name}")


def create():
    obj = Tracked("temp")


create()
print("関数終了")
実行結果
[INIT] temp
関数終了
[DEL] temp

実際にはインタプリタの実装や参照サイクルの有無などで挙動が変わるため、ファイルやネットワークリソースの解放にはwith文と__enter__/__exit__を使うのが安全です。

__str__と__repr__

__str____repr__は、オブジェクトの文字列表現を制御します。

  • __str__(self)
    人間が読むことを前提とした、わかりやすい文字列を返します。print(obj)str(obj)で使われます。
  • __repr__(self)
    開発者向けの詳細な表現で、可能ならeval(repr(obj)) == objが成り立つように記述するのが理想とされています。REPLで変数を評価したときやrepr(obj)で使われます。

実務では__repr__をしっかり作り、__str____repr__を流用することも多いです。

Python
class User:
    def __init__(self, user_id, name, is_admin=False):
        self.user_id = user_id
        self.name = name
        self.is_admin = is_admin

    def __repr__(self):
        # 開発者が状態を一目で確認できるように詳細に書く
        return (f"User(user_id={self.user_id!r}, "
                f"name={self.name!r}, is_admin={self.is_admin!r})")

    def __str__(self):
        # ユーザ向けにはコンパクトな表現
        role = "admin" if self.is_admin else "user"
        return f"{self.name} ({role})"


u = User(1, "Alice", True)
print(u)          # __str__
print(repr(u))    # __repr__
実行結果
Alice (admin)
User(user_id=1, name='Alice', is_admin=True)

__format__

__format__(self, format_spec)format(obj, spec)f"{obj:spec}"のときに呼ばれます。

独自クラスにカスタム書式指定を提供したい場合に使います。

Python
from datetime import datetime

class LogRecord:
    def __init__(self, level, message, created=None):
        self.level = level
        self.message = message
        self.created = created or datetime.now()

    def __format__(self, format_spec):
        # format_specに応じて出力を切り替える例
        if format_spec == "short":
            return f"[{self.level}] {self.message}"
        elif format_spec == "time":
            ts = self.created.strftime("%H:%M:%S")
            return f"{ts} | {self.level} | {self.message}"
        else:
            # デフォルトの詳細形式
            ts = self.created.isoformat(timespec="seconds")
            return f"{ts} [{self.level}] {self.message}"


rec = LogRecord("INFO", "system started")
print(f"{rec:short}")
print(f"{rec:time}")
print(f"{rec}")
実行結果
[INFO] system started
12:34:56 | INFO | system started
2025-12-17T12:34:56 [INFO] system started

__bytes__

__bytes__(self)bytes(obj)が呼ばれたときに使われるメソッドで、オブジェクトをバイト列にシリアライズするときの標準的なフックとして利用できます。

Python
class Packet:
    def __init__(self, msg_type, payload: bytes):
        self.msg_type = msg_type
        self.payload = payload

    def __bytes__(self):
        # 単純なプロトコル: [type(1byte)][len(2byte)][payload]
        length = len(self.payload)
        header = bytes([self.msg_type]) + length.to_bytes(2, "big")
        return header + self.payload


p = Packet(1, b"hello")
raw = bytes(p)
print(raw)
実行結果
b'\x01\x00\x05hello'

演算子オーバーロードの特殊メソッド一覧

算術演算子の特殊メソッド

代表的な算術演算子と特殊メソッドの対応は次のとおりです。

演算子メソッド説明
+__add__加算
__sub__減算
*__mul__乗算
/__truediv__真の除算(浮動小数)
//__floordiv__切り捨て除算
%__mod__剰余
**__pow__べき乗
-x__neg__単項マイナス
+x__pos__単項プラス
abs(x)__abs__絶対値
Python
class Vector2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        # 型チェックを行い、不正な型にはNotImplementedを返すのが慣習
        if not isinstance(other, Vector2D):
            return NotImplemented
        return Vector2D(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        if not isinstance(other, Vector2D):
            return NotImplemented
        return Vector2D(self.x - other.x, self.y - other.y)

    def __neg__(self):
        return Vector2D(-self.x, -self.y)

    def __abs__(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

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


v1 = Vector2D(1, 2)
v2 = Vector2D(3, 4)
print(v1 + v2)
print(v1 - v2)
print(-v1)
print(abs(v2))
実行結果
Vector2D(4, 6)
Vector2D(-2, -2)
Vector2D(-1, -2)
5.0

右辺演算・インプレース演算の特殊メソッド

右辺演算とインプレース演算には次のようなメソッドがあります。

種類通常右辺インプレース
加算__add____radd____iadd__
減算__sub____rsub____isub__
乗算__mul____rmul____imul__
真の除算__truediv____rtruediv____itruediv__
べき乗__pow____rpow____ipow__
など
  • __radd__(self, other)などの右辺演算メソッドは、左辺の__add__NotImplementedを返したときに呼ばれます。
  • __iadd__(self, other)などのインプレース演算メソッドはa += bのときに優先的に呼ばれ、可能なら自分自身を変更して返す実装にします。
Python
class NumberBox:
    def __init__(self, value):
        self.value = value

    def __add__(self, other):
        if isinstance(other, NumberBox):
            return NumberBox(self.value + other.value)
        if isinstance(other, (int, float)):
            return NumberBox(self.value + other)
        return NotImplemented

    def __radd__(self, other):
        # 右辺加算: int + NumberBoxなど
        return self.__add__(other)

    def __iadd__(self, other):
        # インプレース: 自分を直接書き換える
        if isinstance(other, NumberBox):
            self.value += other.value
        elif isinstance(other, (int, float)):
            self.value += other
        else:
            return NotImplemented
        return self

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


nb = NumberBox(10)
print(5 + nb)     # __radd__
nb += 3           # __iadd__
print(nb)
実行結果
NumberBox(15)
NumberBox(13)

比較演算子の特殊メソッド

比較演算子には次のメソッドがあります。

演算子メソッド説明
==__eq__等価
!=__ne__非等価
<__lt__より小さい
<=__le__以下
>__gt__より大きい
>=__ge__以上

ソート可能なオブジェクトにしたい場合は、少なくとも__lt____eq__を実装します。

Python
class Version:
    def __init__(self, major, minor):
        self.major = major
        self.minor = minor

    def _key(self):
        return (self.major, self.minor)

    def __eq__(self, other):
        if not isinstance(other, Version):
            return NotImplemented
        return self._key() == other._key()

    def __lt__(self, other):
        if not isinstance(other, Version):
            return NotImplemented
        return self._key() < other._key()

    def __repr__(self):
        return f"{self.major}.{self.minor}"


versions = [Version(1, 2), Version(1, 10), Version(1, 3)]
print(sorted(versions))
print(Version(1, 2) < Version(1, 3))
実行結果
[1.2, 1.3, 1.10]
True

ビット演算子の特殊メソッド

ビット演算にも対応する特殊メソッドがあります。

フラグ集合やビットマスクをラップするクラスで使われることが多いです。

演算子メソッド説明
&__and__ビットAND
|__or__ビットOR
^__xor__排他的OR
~x__invert__ビット反転
<<__lshift__左シフト
>>__rshift__右シフト

__bool__

__bool__(self)bool(obj)if obj:のときに呼ばれ、オブジェクトの真偽値を決める役割を持ちます。

未定義の場合は__len__が代わりに参照され、長さ0がFalse、それ以外がTrueになります。

Python
class Config:
    def __init__(self, options):
        self.options = options

    def __bool__(self):
        # 有効なオプションが1つ以上あればTrueとみなす
        return bool(self.options)


cfg = Config({})
if not cfg:
    print("設定が空です")
実行結果
設定が空です

コンテナ・イテレータ関連の特殊メソッド一覧

__len__と__contains__

Python
class TagSet:
    def __init__(self, *tags):
        self._tags = set(tags)

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

    def __contains__(self, item):
        # 大文字小文字を区別しないタグ集合とする例
        return item.lower() in {t.lower() for t in self._tags}


tags = TagSet("Python", "Django")
print(len(tags))
print("python" in tags)
実行結果
2
True

__getitem__と__setitem__と__delitem__

__getitem__(self, key)obj[key]で呼ばれ、__setitem__(self, key, value)obj[key] = value__delitem__(self, key)del obj[key]で呼ばれます。

Python
class Settings:
    def __init__(self):
        self._data = {}

    def __getitem__(self, key):
        # 存在しないときにわかりやすいエラーを出す
        try:
            return self._data[key]
        except KeyError:
            raise KeyError(f"設定キーがありません: {key!r}")

    def __setitem__(self, key, value):
        self._data[key] = value

    def __delitem__(self, key):
        del self._data[key]


s = Settings()
s["timeout"] = 30
print(s["timeout"])
del s["timeout"]
実行結果
30

スライス操作用の特殊メソッド

スライスは専用のメソッドがあるわけではなく__getitem__などにsliceオブジェクトが渡されます。

Python
class RangeView:
    def __init__(self, data):
        self._data = list(data)

    def __getitem__(self, index):
        if isinstance(index, slice):
            # スライス用の処理
            return RangeView(self._data[index])
        else:
            return self._data[index]

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

    def __repr__(self):
        return f"RangeView({self._data!r})"


rv = RangeView(range(10))
print(rv[2])
print(rv[2:6])
実行結果
2
RangeView([2, 3, 4, 5])

__iter__と__next__

Python
class Countdown:
    def __init__(self, start):
        self.current = start

    def __iter__(self):
        # 自分自身がイテレータでもあるケース
        return self

    def __next__(self):
        if self.current <= 0:
            raise StopIteration
        value = self.current
        self.current -= 1
        return value


for i in Countdown(3):
    print(i)
実行結果
3
2
1

__reversed__

__reversed__(self)reversed(obj)で呼ばれます。

未定義の場合、__len____getitem__で逆順にアクセスしようとしますが、効率的な逆順イテレータを提供したい場合は明示的に実装します。

Python
class History:
    def __init__(self):
        self._events = []

    def add(self, event):
        self._events.append(event)

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

    def __reversed__(self):
        return reversed(self._events)


h = History()
h.add("start")
h.add("process")
h.add("end")

print(list(h))
print(list(reversed(h)))
実行結果
['start', 'process', 'end']
['end', 'process', 'start']

__missing__

__missing__(self, key)dictサブクラス専用のフックで、存在しないキーを参照したときに呼ばれます。

Python
class DefaultDict(dict):
    def __init__(self, default_factory, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.default_factory = default_factory

    def __missing__(self, key):
        value = self.default_factory()
        # 自動的に登録しておく
        self[key] = value
        return value


d = DefaultDict(list)
d["errors"].append("first error")
print(d)
実行結果
{'errors': ['first error']}

属性アクセス・コンテキスト管理の特殊メソッド一覧

__getattr__と__getattribute__

Python
class LazyAttrs:
    def __init__(self):
        self._cache = {}

    def __getattr__(self, name):
        # 存在しない属性にアクセスされたときだけ呼ばれる
        if name == "expensive":
            print("計算中...")
            value = sum(range(10_000))
            self._cache[name] = value
            return value
        raise AttributeError(name)

    def __getattribute__(self, name):
        # すべてのアクセスを横取り
        if name == "_cache":
            # 無限再帰を避けるためsuperを使う
            return super().__getattribute__(name)
        if name in super().__getattribute__("_cache"):
            return self._cache[name]
        return super().__getattribute__(name)


obj = LazyAttrs()
print(obj.expensive)  # 初回は計算
print(obj.expensive)  # 2回目以降はキャッシュから
実行結果
計算中...
49995000
49995000
注意

__getattribute__は強力ですが、実装を誤ると無限再帰になりやすいため、必要な場合だけ慎重に使うべきです。

__setattr__と__delattr__

  • __setattr__(self, name, value)obj.name = valueで呼ばれます。
  • __delattr__(self, name)del obj.nameで呼ばれます。
Python
class ReadOnlyOnce:
    def __init__(self):
        super().__setattr__("_locked", False)

    def __setattr__(self, name, value):
        if name == "value" and getattr(self, "_locked", False):
            raise AttributeError("valueは一度しか設定できません")
        super().__setattr__(name, value)
        if name == "value":
            super().__setattr__("_locked", True)


obj = ReadOnlyOnce()
obj.value = 10
print(obj.value)
# 次は例外
# obj.value = 20
実行結果
10

__dir__

__dir__(self)dir(obj)で呼ばれ、インタラクティブシェルでの補完候補などを制御できます。

Python
class PublicAPI:
    def __init__(self):
        self._internal = 1
        self.visible = 2

    def __dir__(self):
        # 公開したい属性だけを返す
        return ["visible"]


p = PublicAPI()
print(dir(p))
実行結果
['visible']

__enter__と__exit__

__enter__(self)__exit__(self, exc_type, exc, tb)は、コンテキストマネージャを実装するためのメソッドです。

Python
class FileLogger:
    def __init__(self, path):
        self.path = path
        self._f = None

    def __enter__(self):
        self._f = open(self.path, "a", encoding="utf-8")
        self._f.write("=== start ===\n")
        return self

    def __exit__(self, exc_type, exc, tb):
        if exc:
            self._f.write(f"ERROR: {exc}\n")
        self._f.write("=== end ===\n")
        self._f.close()
        # 例外を抑制しない場合はFalseを返す
        return False

    def log(self, msg):
        self._f.write(msg + "\n")


with FileLogger("log.txt") as logger:
    logger.log("hello")
実行結果
# log.txt の中身(例)
=== start ===
hello
=== end ===

デスクリプタプロトコル

デスクリプタは、属性アクセスの振る舞いをクラス属性側に委ねる仕組みです。

次の3つのメソッドから構成されます。

  • __get__(self, instance, owner)
  • __set__(self, instance, value)
  • __delete__(self, instance)
Python
class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        if instance is None:
            return self  # クラスからアクセスされた場合
        return instance.__dict__.get(self.name)

    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            raise TypeError(f"{self.name} must be {self.expected_type}")
        instance.__dict__[self.name] = value


class Person:
    age = Typed("age", int)

    def __init__(self, age):
        self.age = age


p = Person(20)
print(p.age)
# p.age = "old"  # TypeError
実行結果
20

propertyも内部的にはデスクリプタであり、@propertyで作られたオブジェクトは__get__/__set__を持っています。

クラス・インスタンス挙動を変える特殊メソッド一覧

__call__

__call__(self, ...)を実装すると、インスタンスを関数のように呼び出せるようになります。

Python
class Multiplier:
    def __init__(self, factor):
        self.factor = factor

    def __call__(self, x):
        return x * self.factor


double = Multiplier(2)
print(double(10))
実行結果
20

__instancecheck__と__subclasscheck__

これらは主に抽象基底クラス(ABC)で使われ、isinstanceissubclassの挙動をカスタマイズします。

通常はabc.ABCMetaを使うため、直接実装する機会は多くありません。

メタクラス関連の特殊メソッド

メタクラスは「クラスを生成するクラス」です。

メタクラス側で次のような特殊メソッドを実装できます。

  • __new__(mcls, name, bases, namespace)
    クラスオブジェクト生成の前処理。
  • __init__(cls, name, bases, namespace)
    クラスオブジェクトの初期化。
  • __call__(cls, *args, **kwargs)
    クラス呼び出し時(インスタンス生成)の挙動をカスタマイズ。
Python
class RegistryMeta(type):
    registry = {}

    def __new__(mcls, name, bases, namespace):
        cls = super().__new__(mcls, name, bases, namespace)
        if name != "Base":
            RegistryMeta.registry[name] = cls
        return cls


class Base(metaclass=RegistryMeta):
    pass


class ServiceA(Base):
    pass


class ServiceB(Base):
    pass


print(RegistryMeta.registry)
実行結果
{'ServiceA': <class '__main__.ServiceA'>, 'ServiceB': <class '__main__.ServiceB'>}

Python特殊メソッドの実用パターン集

数値クラス・ベクトルクラスでの演算子オーバーロード例

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

    def _pair(self):
        return (self.x, self.y)

    def __add__(self, other):
        if not isinstance(other, Vec2):
            return NotImplemented
        return Vec2(self.x + other.x, self.y + other.y)

    def __mul__(self, scalar):
        if not isinstance(scalar, (int, float)):
            return NotImplemented
        return Vec2(self.x * scalar, self.y * scalar)

    def __rmul__(self, scalar):
        return self.__mul__(scalar)

    def __abs__(self):
        x, y = self._pair()
        return (x * x + y * y) ** 0.5

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


v1 = Vec2(1, 2)
v2 = Vec2(3, 4)
print(v1 + v2)
print(2 * v1)
print(abs(v2))
実行結果
Vec2(4, 6)
Vec2(2, 4)
5.0

設定オブジェクト・DTOへの__repr__活用例

Python
class AppConfig:
    def __init__(self, host, port, debug=False):
        self.host = host
        self.port = port
        self.debug = debug

    def __repr__(self):
        return (f"AppConfig(host={self.host!r}, "
                f"port={self.port!r}, debug={self.debug!r})")


cfg = AppConfig("localhost", 8000, True)
print(cfg)
実行結果
AppConfig(host='localhost', port=8000, debug=True)

DTO(Data Transfer Object)や設定オブジェクトでは__repr__を整えるだけで、ログやインタラクティブデバッグの効率が大きく向上します。

カスタムコレクションでの__len__・__iter__実装例

Python
class TaskList:
    def __init__(self):
        self._tasks = []

    def add(self, task):
        self._tasks.append(task)

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

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

    def __repr__(self):
        return f"TaskList({len(self)} tasks)"


tasks = TaskList()
tasks.add("backup")
tasks.add("cleanup")

print(tasks)
for t in tasks:
    print("-", t)
print("count:", len(tasks))
実行結果
TaskList(2 tasks)
- backup
- cleanup
count: 2

len・for・inが自然に使えることは、コレクション設計の基本です。

ロガー・トレーサーでの__enter__・__exit__活用例

Python
import time

class ScopeTracer:
    def __init__(self, name):
        self.name = name

    def __enter__(self):
        self.start = time.time()
        print(f"[ENTER] {self.name}")
        return self

    def __exit__(self, exc_type, exc, tb):
        elapsed = time.time() - self.start
        if exc:
            print(f"[ERROR] {self.name}: {exc}")
        print(f"[EXIT] {self.name} ({elapsed:.3f}s)")
        return False  # 例外はそのまま伝播


def work():
    with ScopeTracer("work"):
        time.sleep(0.1)


work()
実行結果
[ENTER] work
[EXIT] work (0.100s)

APIクライアントでの__call__活用例

Python
import urllib.parse

class ApiClient:
    def __init__(self, base_url):
        self.base_url = base_url.rstrip("/")

    def __call__(self, path, **params):
        url = f"{self.base_url}/{path.lstrip('/')}"
        if params:
            query = urllib.parse.urlencode(params)
            url = f"{url}?{query}"
        # 実際にはrequests.getなどを呼ぶ
        print(f"GET {url}")
        return {"url": url, "status": "ok"}


client = ApiClient("https://api.example.com")
res = client("users", id=1)
print(res)
実行結果
GET https://api.example.com/users?id=1
{'url': 'https://api.example.com/users?id=1', 'status': 'ok'}

APIクライアントを関数のように扱えるため、DSL的な書き心地を実現できます。

まとめ

Pythonの特殊メソッドは、演算子オーバーロードからコンテナ挙動、コンテキスト管理、属性アクセス制御、メタクラスまで、言語の核となる仕組みを一通りカバーしています。

本記事で紹介した主な特殊メソッドの一覧と実用パターンを押さえておけば、自作クラスを標準ライブラリと同じような感覚で自然に扱えるようになります。

設計時に「ここはlenで長さを取りたい」「ここはwithで安全に扱いたい」と感じたら、その裏にある特殊メソッドを思い出し、必要なフックを実装していくとよいです。

クラスとオブジェクト指向

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!