Python | OOP:repr

Python Python
スポンサーリンク

概要(reprは「開発者向けの公式な文字列表現」)

reprは、オブジェクトの“正確で曖昧さのない”文字列表現を返す特別メソッドです。REPLやデバッグ、ログでの診断に使われ、可能ならその文字列からオブジェクトを再構築できる形(再現可能性)を目指します。対になるstrが“人向けの読みやすい表示”なのに対し、reprは“開発者向けの確実な表示”です。


基本の使い方(print/repr/REPLでの挙動を理解する)

最小例とREPLでの表示

class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
    def __repr__(self) -> str:
        return f"Person(name={self.name!r}, age={self.age!r})"

p = Person("太郎", 23)
print(repr(p))   # Person(name='太郎', age=23)
p                 # REPLでは __repr__ が表示に使われる
Python

REPL(対話環境)やloggingの詳細出力ではreprが使われます。主要属性を含め、型名がわかる形式で返すのが基本です。

strとの違い(役割の線引き)

class Price:
    def __init__(self, amount: float, currency: str = "JPY"):
        self.amount = amount
        self.currency = currency
    def __str__(self) -> str:
        return f"{self.amount:.2f} {self.currency}"          # 人向けの簡潔表示
    def __repr__(self) -> str:
        return f"Price(amount={self.amount!r}, currency={self.currency!r})"  # 開発者向け
Python

UIやユーザ向けにはstr、デバッグやREPLにはrepr。両方を用途に合わせて実装すると、表示で迷わなくなります。


設計のポイント(再現可能性・曖昧さ排除・安全性)

再現可能性を意識する

「可能なら」repr文字列を評価すれば同等のオブジェクトを作れる形を目指します。最低限、クラス名と主要属性を明示し、文字列は!rでクォートを含めて曖昧さを排除します。

class Rect:
    def __init__(self, w: float, h: float):
        self.w = w; self.h = h
    def __repr__(self) -> str:
        return f"Rect(w={self.w!r}, h={self.h!r})"
Python

機微情報は出さない(安全なrepr)

APIキーやトークンなどをそのままreprに出すのは危険です。必要なら要約・マスクで表現し、完全な値は表示しない方針を守ります。

class ApiKey:
    def __init__(self, token: str):
        self.token = token
    def __repr__(self) -> str:
        t = self.token
        masked = (t[:4] + "..." + t[-4:]) if len(t) > 8 else "***"
        return f"ApiKey(token={masked!r})"
Python

長さと安定性を保つ

大量要素のコレクションを丸ごとreprに入れると読みにくくなります。件数や一部要約、上限表示で“読めるサイズ”に保つのが実践的です。


実務での使い方(モデル・クライアント・例外の診断性向上)

モデルのreprで状態を一目で把握する

class UserModel:
    def __init__(self, uid: str, name: str | None = None, role: str = "user"):
        self.uid = uid
        self.name = name or "unknown"
        self.role = role
    def __repr__(self) -> str:
        return f"UserModel(uid={self.uid!r}, name={self.name!r}, role={self.role!r})"
Python

ログに出した瞬間、何が入っているかが明確になります。

クライアントの接続概要をreprで短く明確に

import requests

class ApiClient:
    def __init__(self, base_url: str, timeout: float = 5.0):
        self.base_url = base_url
        self.timeout = timeout
        self.session = requests.Session()
    def __repr__(self) -> str:
        return f"ApiClient(base_url={self.base_url!r}, timeout={self.timeout!r})"
Python

動作に重要な設定だけを明示し、セキュア情報は含めないのが鉄則です。

例外型のreprで原因追跡を助ける

class ServiceError(Exception):
    def __init__(self, message: str, uid: str | None = None):
        super().__init__(message)
        self.uid = uid
    def __repr__(self) -> str:
        return f"ServiceError(message={self.args[0]!r}, uid={self.uid!r})"
Python

ログで例外が並んでも、どのケースか即判別できます。


深掘りトピック(コンテナのrepr・dataclass・フォールバック)

コンテナ内での表示

listやdictにオブジェクトが入ると、それらのreprが使われます。つまりクラスにreprが無いと、リスト表示が読みにくくなります。代表クラスにはreprを必ず用意しましょう。

dataclassのrepr自動生成

単純なデータ容器ならdataclassを使えば、フィールドを含むreprが自動生成されます。独自の表示にしたい場合はreprを上書きします。

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float
# repr: Point(x=1.0, y=2.0)
Python

strへのフォールバック

reprが未実装でも、str()が用意されていればそちらが表示に使われる場面があります。ただしREPLは通常reprを使うため、デバッグ重視ならreprを先に整えるのが近道です。


例題(注文・ユーザー・クライアントのreprでデバッグ効率を上げる)

一貫したreprを揃える

class Order:
    def __init__(self, order_id: str, items: list[tuple[str, int]]):
        self.order_id = order_id
        self.items = items
        self.status = "init"
    def __repr__(self) -> str:
        return f"Order(order_id={self.order_id!r}, status={self.status!r}, items={len(self.items)!r})"

class User:
    def __init__(self, uid: str, name: str | None = None):
        self.uid = uid
        self.name = name or "unknown"
    def __repr__(self) -> str:
        return f"User(uid={self.uid!r}, name={self.name!r})"

import requests
class Client:
    def __init__(self, base_url: str, key: str, timeout: float = 5.0):
        self.base_url = base_url
        self.key = key
        self.timeout = timeout
        self.session = requests.Session()
    def __repr__(self) -> str:
        return f"Client(base_url={self.base_url!r}, timeout={self.timeout!r})"
Python

ログに並べても判読しやすく、重要情報だけが揃うため、原因究明が速くなります。


まとめ(reprは「開発者のための真面目な顔」)

reprは、型名・主要属性・曖昧さ排除(!r)を備えた、開発者向けの公式表示です。可能なら再現可能性を意識し、機微情報は出さない。大量データは要約し、REPLやログでの見通しを最優先にする。strと役割を分け、代表的なクラスには必ずreprを用意する。これだけで、デバッグの速さと品質が一段上がります。

タイトルとURLをコピーしました