__str__ と __repr__:オブジェクトの文字列表現
Python のオブジェクトを print() したとき、何が表示されるかを決めるのが __str__ と __repr__ です。どちらも文字列を返すメソッドですが、目的が違う。
何も定義しないとどうなるか
まずデフォルトの挙動を見てみます。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
user = User("Alice", 30)
print(user) # <__main__.User object at 0x7f...>
クラス名とメモリアドレスが表示される。これでは何のオブジェクトかわからない。
str:人間向けの表現
__str__ は print() や str() で呼ばれます。人間が読むための、わかりやすい文字列を返します。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}({self.age}歳)"
user = User("Alice", 30)
print(user) # Alice(30歳)
print(str(user)) # Alice(30歳)
ユーザーに見せる文字列を意識して書く。フォーマットは自由。
repr:開発者向けの表現
__repr__ はオブジェクトの「公式な」文字列表現を返します。デバッグやログ出力で使われることを想定しています。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"User(name={self.name!r}, age={self.age!r})"
user = User("Alice", 30)
print(repr(user)) # User(name='Alice', age=30)
慣習として、__repr__ はそのオブジェクトを再現できるような文字列を返すのが理想。eval(repr(obj)) で同じオブジェクトを作れれば完璧ですが、必須ではありません。
両方定義する
通常は両方定義します。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}({self.age}歳)"
def __repr__(self):
return f"User(name={self.name!r}, age={self.age!r})"
user = User("Alice", 30)
print(user) # Alice(30歳)
print(repr(user)) # User(name='Alice', age=30)
人間が読むための表現。print() や str() で呼ばれる。わかりやすさ重視。
開発者がデバッグするための表現。repr() や対話モードで呼ばれる。正確さ重視。
str がないとき repr が使われる
__str__ を定義せず __repr__ だけ定義した場合、print() は __repr__ を使います。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"User(name={self.name!r}, age={self.age!r})"
user = User("Alice", 30)
print(user) # User(name='Alice', age=30)(__repr__ が使われる)
逆に __str__ だけ定義して __repr__ を定義しない場合、repr() はデフォルトの <__main__.User object at ...> を返します。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}({self.age}歳)"
user = User("Alice", 30)
print(user) # Alice(30歳)
print(repr(user)) # <__main__.User object at 0x7f...>
だから「どちらか一つだけ定義するなら __repr__ を優先せよ」と言われる。__repr__ があれば __str__ のフォールバックとして機能するからです。
コンテナの中では repr が使われる
リストや辞書の中にオブジェクトを入れて表示すると、__repr__ が呼ばれます。
class User:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def __repr__(self):
return f"User({self.name!r})"
users = [User("Alice"), User("Bob")]
print(users) # [User('Alice'), User('Bob')](__repr__ が使われる)
for user in users:
print(user) # Alice / Bob(__str__ が使われる)
リストの __repr__ が各要素の __repr__ を呼び出している。この挙動を知らないと混乱しがち。
f-string と format での挙動
f-string や format() では __str__ が使われますが、!r を付けると __repr__ を強制できます。
class User:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def __repr__(self):
return f"User({self.name!r})"
user = User("Alice")
print(f"{user}") # Alice(__str__)
print(f"{user!r}") # User('Alice')(__repr__)
print(f"{user!s}") # Alice(__str__ を明示)
ログ出力では !r を使うと、文字列なのか他の型なのかが明確になって便利です。
name = "Alice"
age = 30
# !r なし:型がわからない
print(f"name={name}, age={age}") # name=Alice, age=30
# !r あり:文字列は引用符付きで表示される
print(f"name={name!r}, age={age!r}") # name='Alice', age=30
実践的な書き方
よくあるパターンをまとめます。
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})"
class Person:
def __init__(self, name, email):
self.name = name
self.email = email
def __str__(self):
return self.name
def __repr__(self):
return f"Person(name={self.name!r}, email={self.email!r})"
class APIResponse:
def __init__(self, status, data):
self.status = status
self.data = data
def __str__(self):
return f"Status: {self.status}"
def __repr__(self):
# data が大きい場合は省略
data_repr = repr(self.data)
if len(data_repr) > 50:
data_repr = data_repr[:50] + "..."
return f"APIResponse(status={self.status}, data={data_repr})"
まとめ
デバッグに必須。オブジェクトの状態を正確に表す文字列を返す。__str__ がなければ print() でも使われる。
ユーザー向けの表示が必要なときに。わかりやすさを重視して、技術的な詳細は省いてよい。
迷ったら __repr__ だけ定義すれば十分。両方定義するなら、__repr__ は正確に、__str__ は簡潔に。