- Python
Python __str__と__repr__の違い|特殊メソッド一覧
__str__ は「人に見せるための文字列」、__repr__ は「開発者が中身を確かめるための文字列」を返す特殊メソッドです。 print() や str() は __str__ を、repr()・対話シェル・リストや辞書の中の表示は __repr__ を呼びます。そして __str__ を定義していないクラスでは、print() も __repr__ の結果を使います。
この記事では、print・str()・f文字列・f文字列の !r・repr()・対話シェル・リストや辞書の中・logging・例外メッセージのそれぞれでどちらが呼ばれるかを、Python 3.10.18/3.11.15/3.14.7で実行して早見表にしました。片方だけ定義した場合や <__main__.X object at 0x...> と表示される場合の4パターン、__init__・__eq__・__hash__・__lt__・__len__・__getitem__・__iter__・__enter__ などを「いつ呼ばれるか」で整理した特殊メソッド一覧、dataclass が自動で作るメソッドの対応表まで、コピーして動かせるコードつきでまとめています。
どちらが呼ばれるかの早見表
次のクラスのインスタンス b を、いろいろな方法で表示した結果です(Python 3.11.15。3.10.18と3.14.7でも同じ出力でした)。
__str__の戻り値:『Python入門』2800円__repr__の戻り値:Book(title='Python入門', price=2800)
| 表示の方法 | コード | 呼ばれるもの | 実行結果 |
|---|---|---|---|
print(b) |
__str__ |
『Python入門』2800円 |
|
| str() | str(b) |
__str__ |
『Python入門』2800円 |
| repr() | repr(b) |
__repr__ |
Book(title='Python入門', price=2800) |
| f文字列 | f"{b}" |
__str__ |
『Python入門』2800円 |
f文字列の !r |
f"{b!r}" |
__repr__ |
Book(title='Python入門', price=2800) |
f文字列の = |
f"{b=}" |
__repr__ |
b=Book(title='Python入門', price=2800) |
| format() | "{}".format(b) |
__str__ |
『Python入門』2800円 |
| % 書式 | "%s / %r" % (b, b) |
%s は __str__、%r は __repr__ |
『Python入門』2800円 / Book(title='Python入門', price=2800) |
| 対話シェルで変数名だけ入力 | >>> b |
__repr__ |
Book('Python入門')(後述の別クラスでの結果) |
| リストの中 | print([b]) |
__repr__ |
[Book(title='Python入門', price=2800)] |
| 辞書の中 | print({"key": b}) |
__repr__ |
{'key': Book(title='Python入門', price=2800)} |
| タプルの中 | print((b,)) |
__repr__ |
(Book(title='Python入門', price=2800),) |
logging の %s |
logging.info("%s", b) |
__str__ |
『Python入門』2800円 |
logging の %r |
logging.info("%r", b) |
__repr__ |
Book(title='Python入門', price=2800) |
| 例外メッセージ | str(ValueError(b)) |
__str__ |
『Python入門』2800円 |
| 例外の repr | repr(ValueError(b)) |
__repr__ |
ValueError(Book(title='Python入門', price=2800)) |
| KeyError のメッセージ | str(KeyError(b)) |
__repr__ |
Book(title='Python入門', price=2800) |
覚え方は「直接表示するときは __str__、何かの中に入っているときと、開発者向けの表示は __repr__」 です。
定義の有無による違いは次の4パターンです(詳しい実行結果は後の章にあります)。
| 定義したもの | print(obj) |
repr(obj)・リストの中 |
|---|---|---|
| どちらも定義しない | <__main__.Neither object at 0x...> |
<__main__.Neither object at 0x...> |
__str__ だけ |
__str__ の結果 |
<__main__.OnlyStr object at 0x...> |
__repr__ だけ |
__repr__ の結果 |
__repr__ の結果 |
| 両方 | __str__ の結果 |
__repr__ の結果 |
1つだけ書くなら __repr__ です。print でもリストの中でも読める表示になります。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python __str__と__repr__の違い|特殊メソッド一覧」
確認した環境
| 項目 | 内容 |
|---|---|
| OS | Linux(コンテナ) |
| 主に使った版 | Python 3.11.15 |
| 比べた版 | Python 3.10.18/3.14.7 |
| 使ったもの | 標準ライブラリのみ(logging、functools、dataclasses) |
とくに断りのない出力は Python 3.11.15 のものです。0x7f... のアドレスは実行のたびに変わります。トレースバック内のファイルパスは短い名前に置き換えています。
早見表のもとになったコードと実行結果
早見表は次のコードの出力から作りました。そのまま貼って動きます。
import logging, sys
class Book:
def __init__(self, title, price):
self.title = title
self.price = price
def __str__(self):
return f"『{self.title}』{self.price}円"
def __repr__(self):
return f"Book(title={self.title!r}, price={self.price!r})"
b = Book("Python入門", 2800)
print("1 print :", end=" "); print(b)
print("2 str() :", str(b))
print("3 repr() :", repr(b))
print("4 f文字列 :", f"{b}")
print("5 f文字列 !r :", f"{b!r}")
print("6 f文字列 !s :", f"{b!s}")
print("7 f文字列 = :", f"{b=}")
print("8 format() :", "{}".format(b), "/", "{!r}".format(b), "/", format(b))
print("9 % 書式 :", "%s / %r" % (b, b))
print("10 リスト :", [b])
print("11 辞書 :", {"key": b})
print("12 タプル :", (b,))
print("13 str(list) :", str([b]))
print("14 join :", ", ".join(map(str, [b, b])))
print("15 文字列連結:", "本: " + str(b))
logging.basicConfig(format="%(levelname)s %(message)s", stream=sys.stdout, level=logging.INFO)
logging.info("16 logging %%s: %s", b)
logging.info("17 logging %%r: %r", b)
logging.info(b)
try:
raise ValueError(b)
except ValueError as e:
print("19 例外 str(e) :", str(e))
print("20 例外 repr(e):", repr(e))
print("21 例外 e.args :", e.args)
try:
raise KeyError(b)
except KeyError as e:
print("22 KeyError str(e):", str(e))
try:
assert False, b
except AssertionError as e:
print("23 assert :", e)
try:
"x" + b
except TypeError as e:
print("24 連結エラー :", e)
実行結果:
1 print : 『Python入門』2800円
2 str() : 『Python入門』2800円
3 repr() : Book(title='Python入門', price=2800)
4 f文字列 : 『Python入門』2800円
5 f文字列 !r : Book(title='Python入門', price=2800)
6 f文字列 !s : 『Python入門』2800円
7 f文字列 = : b=Book(title='Python入門', price=2800)
8 format() : 『Python入門』2800円 / Book(title='Python入門', price=2800) / 『Python入門』2800円
9 % 書式 : 『Python入門』2800円 / Book(title='Python入門', price=2800)
10 リスト : [Book(title='Python入門', price=2800)]
11 辞書 : {'key': Book(title='Python入門', price=2800)}
12 タプル : (Book(title='Python入門', price=2800),)
13 str(list) : [Book(title='Python入門', price=2800)]
14 join : 『Python入門』2800円, 『Python入門』2800円
15 文字列連結: 本: 『Python入門』2800円
INFO 16 logging %s: 『Python入門』2800円
INFO 17 logging %r: Book(title='Python入門', price=2800)
INFO 『Python入門』2800円
19 例外 str(e) : 『Python入門』2800円
20 例外 repr(e): ValueError(Book(title='Python入門', price=2800))
21 例外 e.args : (Book(title='Python入門', price=2800),)
22 KeyError str(e): Book(title='Python入門', price=2800)
23 assert : 『Python入門』2800円
24 連結エラー : can only concatenate str (not "Book") to str
気づきにくい点を補足します。
| 番号 | 補足 |
|---|---|
| 7 | f"{b=}" は「式=値」を表示する書き方で、値の側は __repr__ が使われる。確認用の出力に向いている |
| 10〜13 | リスト・辞書・タプルは、自分自身を文字列にするとき要素の __repr__ を呼ぶ。str([b]) でも同じ |
| 14 | 要素を __str__ で並べたいときは ", ".join(map(str, リスト)) |
18(logging.info(b)) |
メッセージにオブジェクトをそのまま渡すと __str__ が使われる |
| 22 | KeyError は見つからなかったキーを repr で表示するため、__repr__ の結果になる |
| 24 | "x" + b は自動では文字列にならず TypeError。str(b) で明示的に変換する |
例外を送出したときのトレースバック
例外にオブジェクトを渡してそのまま送出すると、最終行には __str__ の結果が出ます。
class Book:
def __init__(self, title):
self.title = title
def __str__(self):
return f"『{self.title}』"
def __repr__(self):
return f"Book(title={self.title!r})"
raise ValueError(Book("Python入門"))
Traceback (most recent call last):
File "book.py", line 9, in <module>
raise ValueError(Book("Python入門"))
ValueError: 『Python入門』
対話シェルでの表示
対話シェル(python3 を引数なしで起動した画面)では、変数名だけを入力すると __repr__ が使われます。Python 3.11.15で次の入力を流した結果です。
>>> class Book:
... def __init__(self, t): self.t = t
... def __str__(self): return f"『{self.t}』"
... def __repr__(self): return f"Book({self.t!r})"
...
>>> b = Book("Python入門")
>>> b
Book('Python入門')
>>> print(b)
『Python入門』
>>> [b]
[Book('Python入門')]
>>> str(b)
'『Python入門』'
最後の str(b) の結果に引用符が付いているのは、対話シェルが「str(b) が返した文字列」をさらに repr で表示しているためです。対話シェルやデバッガで変数を確かめるときに見えるのは __repr__、と覚えておくと混乱しません。3.13以降の新しい対話シェルでの表示は今回は未実行です。
4パターンで比べる:定義の有無と表示
__str__ と __repr__ を「どちらも無し/片方だけ/両方」の4通りで定義して比べます。
class Neither:
def __init__(self, v): self.v = v
class OnlyStr:
def __init__(self, v): self.v = v
def __str__(self): return f"str:{self.v}"
class OnlyRepr:
def __init__(self, v): self.v = v
def __repr__(self): return f"OnlyRepr({self.v!r})"
class Both:
def __init__(self, v): self.v = v
def __str__(self): return f"str:{self.v}"
def __repr__(self): return f"Both({self.v!r})"
for cls in (Neither, OnlyStr, OnlyRepr, Both):
o = cls(1)
print(f"--- {cls.__name__}")
print("print :", o)
print("repr :", repr(o))
print("list :", [o])
--- Neither
print : <__main__.Neither object at 0x7f6386d2a7d0>
repr : <__main__.Neither object at 0x7f6386d2a7d0>
list : [<__main__.Neither object at 0x7f6386d2a7d0>]
--- OnlyStr
print : str:1
repr : <__main__.OnlyStr object at 0x7f6386d2a810>
list : [<__main__.OnlyStr object at 0x7f6386d2a810>]
--- OnlyRepr
print : OnlyRepr(1)
repr : OnlyRepr(1)
list : [OnlyRepr(1)]
--- Both
print : str:1
repr : Both(1)
list : [Both(1)]
なぜこうなるのか
仕組みは2つのルールで説明できます。
- すべてのクラスは
objectから既定の__repr__を受け継いでいる。 その既定の実装が<モジュール名.クラス名 object at アドレス>という文字列を返す - 既定の
__str__は__repr__を呼ぶ。 そのため__str__を書かなければ、printの結果は__repr__と同じになる
Python公式ドキュメントのデータモデルのページ(今回開いて確認)にも、__str__ が定義されていない場合は __repr__ が使われること、__repr__ はできれば「同じ値のオブジェクトを作り直せるPythonの式」に見える文字列にすること、が書かれています。
| パターン | 起きること | 困りやすい場面 |
|---|---|---|
| どちらも無し | どこでも <... object at 0x...> |
中身がまったくわからない |
__str__ だけ |
print は読めるが、リストの中やデバッガでは <... object at 0x...> のまま |
一覧を表示したときに中身が見えない |
__repr__ だけ |
どこでも __repr__ の結果 |
利用者向けの見た目を別にしたいときだけ物足りない |
| 両方 | 場面に応じて使い分けられる | ― |
「print(obj) では読めるのに、print(リスト) にすると <__main__.X object at 0x...> が並ぶ」という現象は、__str__ だけを定義しているときに起きます。__repr__ を足せば解決します。
読みやすい __repr__ と __str__ の書き方
__repr__ のひな形
__repr__ は次の形にしておくと、どのクラスにもそのまま使えます。
class Member:
def __init__(self, name, tags):
self.name, self.tags = name, tags
def __repr__(self):
return f"{type(self).__name__}(name={self.name!r}, tags={self.tags!r})"
m = Member("佐藤", ["a", "b"])
print(repr(m))
print(eval(repr(m)).name)
Member(name='佐藤', tags=['a', 'b'])
佐藤
| 書き方のポイント | 理由 |
|---|---|
クラス名は type(self).__name__ で出す |
子クラスを作ったとき、子クラスの名前で表示される |
各属性に !r を付ける |
文字列に引用符が付き、'1' と 1、空文字と空白の区別がつく |
クラス名(引数=値, ...) の形にする |
そのままコードに貼ってオブジェクトを作り直せる |
| 1行に収める | ログや一覧で読みやすい |
上の例では eval(repr(m)) で同じ内容のオブジェクトを作り直せています。これは「作り直せる形になっている」ことを見せるための確認で、ふだんのコードで外から来た文字列を eval に渡すことはしません。
標準の型も同じ考え方で作られている
import datetime
d = datetime.date(2026, 10, 3)
print(str(d), "|", repr(d))
print(repr("ab\n"), repr(1.0), str(1.0))
2026-10-03 | datetime.date(2026, 10, 3)
'ab\n' 1.0 1.0
str は読みやすい表示(2026-10-03)、repr は作り直せる式(datetime.date(2026, 10, 3))です。文字列の repr は引用符付きで、改行が \n と見える形になります。
| 比べる点 | __str__ |
__repr__ |
|---|---|---|
| 読む人 | 利用者、画面や帳票を見る人 | 開発者、ログやデバッガを見る人 |
| 目指す形 | 自然で読みやすい | あいまいさが無く、できれば作り直せる式 |
| 省略したとき | __repr__ が使われる |
<... object at 0x...> |
| 書く優先度 | 必要になってから | まずこちら |
f文字列で幅や寄せを指定するとき
自作クラスのインスタンスに :>12 のような書式指定を付けると、TypeError になります。!s か str() を挟めば、文字列として書式指定できます。
class Book:
def __init__(self, title):
self.title = title
def __str__(self):
return f"『{self.title}』"
def __repr__(self):
return f"Book({self.title!r})"
b = Book("Python入門")
try:
print(f"[{b:>12}]")
except TypeError as e:
print("TypeError:", e)
print(f"[{b!s:>12}]")
print(f"[{b!r:>20}]")
TypeError: unsupported format string passed to Book.__format__
[ 『Python入門』]
[ Book('Python入門')]
書式指定は __format__ という別の特殊メソッドが受け取ります。既定の __format__ は、指定が空のときだけ str() と同じ結果を返すので、幅や寄せを指定したいときは !s/!r で先に文字列へ変換します。
戻り値は必ず文字列にする
__str__ や __repr__ が文字列以外を返すと、次のエラーになります。
class Bad:
def __str__(self):
return 123
try:
print(Bad())
except TypeError as e:
print("TypeError:", e)
TypeError: __str__ returned non-string (type int)
数値などを返したくなったら str(...) で包むか、f文字列にします。
特殊メソッド一覧:いつ呼ばれるかで整理
__str__ や __repr__ のように、前後を下線2つで囲んだメソッドを特殊メソッド(ダンダーメソッドとも呼びます)と言います。自分で直接呼ぶのではなく、決まった書き方をしたときにPythonが呼び出すメソッドです。
生成・表示
| メソッド | いつ呼ばれるか | 書き方の例 |
|---|---|---|
__init__(self, ...) |
インスタンスを作った直後の初期化 | Book("Python入門", 2800) |
__repr__(self) |
repr()、対話シェル、コンテナの中、!r、%r |
repr(b)、[b] |
__str__(self) |
print()、str()、f文字列、%s |
print(b) |
__format__(self, spec) |
f文字列や format() で書式指定したとき |
f"{b:>10}" |
__bool__(self) |
真偽値として評価されたとき | if b:、bool(b) |
比較・ハッシュ
| メソッド | いつ呼ばれるか | 書き方の例 |
|---|---|---|
__eq__(self, other) |
等しいかを比べるとき | a == b、a in リスト、リスト.count(a) |
__ne__(self, other) |
等しくないかを比べるとき(省略すると __eq__ の逆) |
a != b |
__lt__/__le__/__gt__/__ge__ |
大小を比べるとき | a < b、sorted(...)、max(...) |
__hash__(self) |
ハッシュ値が必要なとき | hash(a)、{a}、{a: 1} |
演算
| メソッド | いつ呼ばれるか | 書き方の例 |
|---|---|---|
__add__/__sub__/__mul__ |
算術演算子を使ったとき | a + b、a - b、a * b |
__truediv__/__floordiv__/__mod__ |
割り算・切り捨て割り算・余り | a / b、a // b、a % b |
__radd__ など |
左側の型が演算に対応していないとき | 1 + a |
__iadd__ など |
累算代入のとき | a += b |
__neg__/__abs__ |
単項演算・絶対値 | -a、abs(a) |
コンテナ・くり返し
| メソッド | いつ呼ばれるか | 書き方の例 |
|---|---|---|
__len__(self) |
長さを求めるとき。__bool__ が無ければ真偽値の判定にも使われる |
len(s)、if s: |
__getitem__(self, key) |
添字やスライスで読むとき | s[0]、s[1:3] |
__setitem__(self, key, value) |
添字で代入するとき | s[0] = x |
__delitem__(self, key) |
添字で削除するとき | del s[0] |
__contains__(self, item) |
含まれるかを調べるとき | x in s |
__iter__(self) |
くり返しを始めるとき | for x in s:、list(s) |
__next__(self) |
次の要素を取り出すとき(イテレータ側) | next(it) |
呼び出し・with文・属性
| メソッド | いつ呼ばれるか | 書き方の例 |
|---|---|---|
__call__(self, ...) |
インスタンスを関数のように呼んだとき | obj(...) |
__enter__(self) |
with ブロックに入るとき |
with obj as x: |
__exit__(self, exc_type, exc, tb) |
with ブロックを出るとき(例外が起きても呼ばれる) |
同上 |
__getattr__(self, name) |
通常の方法で属性が見つからなかったとき | obj.unknown |
__setattr__(self, name, value) |
属性に代入するとき | obj.x = 1 |
__del__(self) |
オブジェクトが破棄されるとき(タイミングは保証されない) | ― |
この表のうち、__init__・__repr__・__str__・__format__・__bool__・__eq__・__hash__・__lt__・__add__・__len__・__getitem__・__setitem__・__delitem__・__contains__・__iter__・__call__・__enter__・__exit__ は、次の章のコードで実際に呼ばれることを確かめています。それ以外は一般に知られた仕様として載せており、今回は未実行です。
比較と演算の特殊メソッドを動かす
バージョン番号を表すクラスで、比較・ハッシュ・演算・書式をまとめて確認します。
from functools import total_ordering
@total_ordering
class Version:
def __init__(self, major, minor): # 生成時
print(f" __init__({major}, {minor})")
self.major, self.minor = major, minor
def __repr__(self):
return f"Version({self.major}, {self.minor})"
def __str__(self):
return f"v{self.major}.{self.minor}"
def __eq__(self, other): # ==
if not isinstance(other, Version):
return NotImplemented
return (self.major, self.minor) == (other.major, other.minor)
def __hash__(self): # hash(), set, dict のキー
return hash((self.major, self.minor))
def __lt__(self, other): # <, sorted
if not isinstance(other, Version):
return NotImplemented
return (self.major, self.minor) < (other.major, other.minor)
def __bool__(self): # if obj:
return (self.major, self.minor) != (0, 0)
def __add__(self, n): # +
return Version(self.major, self.minor + n)
def __format__(self, spec): # f"{obj:spec}"
return f"{self.major}.{self.minor:0{spec or 1}d}"
a, b, c = Version(1, 2), Version(1, 10), Version(1, 2)
print(a == c, a != b, a is c)
print(a < b, a >= b, sorted([b, a]), max(a, b))
print(len({a, b, c}), {a: "first"}[c])
print(bool(Version(0, 0)), bool(a))
print(a + 3, f"{a:03}", f"{a}")
print(a == "v1.2", a.__eq__("v1.2"))
try:
a < 5
except TypeError as e:
print("TypeError:", e)
__init__(1, 2)
__init__(1, 10)
__init__(1, 2)
True True False
True False [Version(1, 2), Version(1, 10)] v1.10
2 first
__init__(0, 0)
False True
__init__(1, 5)
v1.5 1.002 1.2
False NotImplemented
TypeError: '<' not supported between instances of 'Version' and 'int'
読み取れるポイントです。
| 行 | わかること |
|---|---|
a == c が True、a is c が False |
== は __eq__ で中身を比べ、is は同じオブジェクトかを比べる |
a != b が True |
__ne__ を書かなくても、__eq__ の逆として動く |
a >= b が動く |
__lt__ と __eq__ だけ書き、functools.total_ordering が残りの比較を補っている |
sorted([b, a]) |
並べ替えは __lt__ を使う。リストの中の表示は __repr__、max(a, b) を print した表示は __str__ |
len({a, b, c}) が 2 |
__eq__ と __hash__ がそろっているので、同じ中身は集合の中で1つにまとまる |
a == "v1.2" が False |
相手が別の型のとき NotImplemented を返すと、Pythonが「等しくない」と判断する |
a < 5 が TypeError |
大小比較で両方が NotImplemented を返すと、わかりやすい例外になる |
別の型と比べられたときは False を直接返すのではなく NotImplemented を返すのが定石です。相手側のクラスに比較を任せる余地が残ります。
__eq__ と __hash__ の関係
__eq__ を定義するときに一緒に知っておきたいのが __hash__ です。__eq__ だけを定義すると、そのクラスのインスタンスは集合に入れたり辞書のキーにしたりできなくなります。
class P1: # 何も定義しない
def __init__(self, x): self.x = x
class P2: # __eq__ だけ
def __init__(self, x): self.x = x
def __eq__(self, other): return isinstance(other, P2) and self.x == other.x
class P3: # __eq__ と __hash__
def __init__(self, x): self.x = x
def __eq__(self, other): return isinstance(other, P3) and self.x == other.x
def __hash__(self): return hash(self.x)
print("P1:", P1(1) == P1(1), len({P1(1), P1(1)}))
print("P2:", P2(1) == P2(1), P2.__hash__)
try:
{P2(1)}
except TypeError as e:
print("P2 TypeError:", e)
print("P3:", P3(1) == P3(1), len({P3(1), P3(1)}), P3(1) in [P3(1)], P3(1) != P3(2))
print([P1(1)].count(P1(1)), [P3(1)].count(P3(1)))
Python 3.11.15:
P1: False 2
P2: True None
P2 TypeError: unhashable type: 'P2'
P3: True 1 True True
0 1
Python 3.14.7では、3行目が次のように、どの操作で起きたかまで伝える文になっていました。
P2 TypeError: cannot use 'P2' as a set element (unhashable type: 'P2')
| クラス | == の結果 |
集合・辞書のキー | 理由 |
|---|---|---|---|
| P1(何も定義しない) | 中身が同じでも False |
使える(別物として2件) | 既定の __eq__ は「同じオブジェクトか」で比べる |
P2(__eq__ だけ) |
True |
使えない | __eq__ を定義すると __hash__ が自動で None になる |
| P3(両方) | True |
使える(1件にまとまる) | 等しいもの同士が同じハッシュ値を返す |
公式ドキュメントのデータモデルのページにも、__eq__ を上書きして __hash__ を定義しないクラスは __hash__ が暗黙に None になること、等しいと判定されるオブジェクトは同じハッシュ値を持つ必要があることが書かれています。
__hash__ を書くときの目安は次のとおりです。
| 目安 | 内容 |
|---|---|
| 使う属性をそろえる | __eq__ で比べている属性と同じものをタプルにして hash(...) に渡す |
| 変わらない値だけを使う | 集合に入れたあとで属性を書き換えると、探せなくなることがある |
| 変更するクラスなら無理に定義しない | 辞書のキーにしない使い方なら、__hash__ が None のままで問題ない |
コンテナ・with文の特殊メソッドを動かす
次は「本棚」を表すクラスで、len()・添字・in・for・呼び出し・with の動きを確かめます。
class Shelf:
def __init__(self, *books):
self._books = list(books)
def __len__(self): # len(obj), 真偽値の判定
return len(self._books)
def __getitem__(self, index): # obj[i], obj[a:b]
return self._books[index]
def __setitem__(self, index, value): # obj[i] = v
self._books[index] = value
def __delitem__(self, index): # del obj[i]
del self._books[index]
def __contains__(self, item): # x in obj
return item in self._books
def __iter__(self): # for x in obj
return iter(self._books)
def __call__(self, word): # obj(...)
return [b for b in self._books if word in b]
def __enter__(self): # with obj as x:
print(" __enter__")
return self
def __exit__(self, exc_type, exc, tb):
print(" __exit__", exc_type.__name__ if exc_type else None)
return False
s = Shelf("Python入門", "SQL入門", "Git入門")
print(len(s), s[0], s[-1], s[0:2])
s[1] = "Linux入門"; del s[2]
print(list(s), "Linux入門" in s, bool(s), bool(Shelf()))
print(s("Py"))
with Shelf("A") as t:
print(" with の中", len(t))
try:
with Shelf("B"):
raise ValueError("中で例外")
except ValueError as e:
print("ValueError:", e)
3 Python入門 Git入門 ['Python入門', 'SQL入門']
['Python入門', 'Linux入門'] True True False
['Python入門']
__enter__
with の中 1
__exit__ None
__enter__
__exit__ ValueError
ValueError: 中で例外
bool(Shelf())がFalseになっているのは、__bool__が無いときに__len__の結果が0かどうかで真偽が決まるため__exit__は、withの中で例外が起きても呼ばれる。False(または何も返さない)なら例外はそのまま外へ伝わる
特殊メソッドが無いときのエラー文
対応する特殊メソッドを持たないクラスで各操作をしたときのメッセージです。エラー文から「どのメソッドを足せばよいか」を逆引きできます。
| 操作 | エラーメッセージ(Python 3.11.15) | 足すメソッド |
|---|---|---|
len(x) |
TypeError: object of type 'X' has no len() |
__len__ |
x[0] |
TypeError: 'X' object is not subscriptable |
__getitem__ |
list(x)/for |
TypeError: 'X' object is not iterable |
__iter__(または __getitem__) |
x + x |
TypeError: unsupported operand type(s) for +: 'X' and 'X' |
__add__ |
x < x |
TypeError: '<' not supported between instances of 'X' and 'X' |
__lt__ |
x() |
TypeError: 'X' object is not callable |
__call__ |
with x: |
TypeError: 'X' object does not support the context manager protocol |
__enter__ と __exit__ |
{x}(__eq__ だけ定義) |
TypeError: unhashable type: 'P2' |
__hash__ |
with のメッセージは版で違い、Python 3.10.18では AttributeError: __enter__、3.14.7では TypeError: 'X' object does not support the context manager protocol (missed __exit__ method) でした。
なお、__getitem__ だけを定義したクラスも、0から順に添字で読んで IndexError で止まる形なら for や in が使えることを確かめました(list(...) が [0, 10, 20]、20 in ... が True)。ただし len() は __len__ が無いと使えません。
dataclassが自動で作るメソッドの対応表
@dataclass を付けると、ここまで手で書いてきた特殊メソッドの一部が自動で作られます。何が作られ、何が作られないかを、クラスの __dict__ を調べて確かめました。
from dataclasses import dataclass
@dataclass
class A:
name: str
price: int
@dataclass(order=True)
class B:
name: str
price: int
@dataclass(frozen=True)
class C:
name: str
price: int
@dataclass(eq=False)
class D:
name: str
price: int
@dataclass(slots=True)
class E:
name: str
price: int
names = ["__init__", "__repr__", "__eq__", "__lt__", "__le__", "__gt__", "__ge__",
"__hash__", "__setattr__", "__delattr__", "__match_args__", "__slots__", "__str__"]
for cls in (A, B, C, D, E):
made = [n for n in names if n in cls.__dict__]
print(cls.__name__, made, "hash=", cls.__dict__.get("__hash__", "(継承)"))
A ['__init__', '__repr__', '__eq__', '__hash__', '__match_args__'] hash= None
B ['__init__', '__repr__', '__eq__', '__lt__', '__le__', '__gt__', '__ge__', '__hash__', '__match_args__'] hash= None
C ['__init__', '__repr__', '__eq__', '__hash__', '__setattr__', '__delattr__', '__match_args__'] hash= <function C.__hash__ at 0x7fa54edde7a0>
D ['__init__', '__repr__', '__match_args__'] hash= (継承)
E ['__init__', '__repr__', '__eq__', '__hash__', '__match_args__', '__slots__'] hash= None
表にまとめると次のとおりです。
| メソッド | @dataclass |
order=True |
frozen=True |
eq=False |
slots=True |
|---|---|---|---|---|---|
__init__ |
作られる | 作られる | 作られる | 作られる | 作られる |
__repr__ |
作られる | 作られる | 作られる | 作られる | 作られる |
__eq__ |
作られる | 作られる | 作られる | 作られない | 作られる |
__lt__/__le__/__gt__/__ge__ |
作られない | 作られる | 作られない | 作られない | 作られない |
__hash__ |
None(ハッシュ不可) |
None |
作られる | object のものを継承 |
None |
__setattr__/__delattr__ |
作られない | 作られない | 作られる(代入を止める) | 作られない | 作られない |
__match_args__ |
作られる | 作られる | 作られる | 作られる | 作られる |
__slots__ |
作られない | 作られない | 作られない | 作られない | 作られる |
__str__ |
作られない | 作られない | 作られない | 作られない | 作られない |
__str__ はどの指定でも作られません。 print したときは、自動で作られた __repr__ が使われます。動きも確かめます。
a = A("ノート", 300)
print(a, "|", repr(a), "|", f"{a}", "|", [a])
print(a == A("ノート", 300), A.__str__ is object.__str__)
try:
hash(a)
except TypeError as e:
print("TypeError:", e)
print(hash(C("ノート", 300)) == hash(C("ノート", 300)), len({C("a", 1), C("a", 1)}))
print(B("a", 2) < B("b", 1), sorted([B("b", 1), B("a", 2)]))
try:
a < a
except TypeError as e:
print("TypeError:", e)
print(D("a", 1) == D("a", 1))
@dataclass
class Money:
amount: int
unit: str = "円"
def __str__(self):
return f"{self.amount:,}{self.unit}"
m = Money(12000)
print(m, "|", repr(m), "|", [m])
A(name='ノート', price=300) | A(name='ノート', price=300) | A(name='ノート', price=300) | [A(name='ノート', price=300)]
True True
TypeError: unhashable type: 'A'
True 1
True [B(name='a', price=2), B(name='b', price=1)]
TypeError: '<' not supported between instances of 'A' and 'A'
False
12,000円 | Money(amount=12000, unit='円') | [Money(amount=12000, unit='円')]
| わかること | 内容 |
|---|---|
| 既定の dataclass は集合に入れられない | __eq__ が作られ、__hash__ が None になるため(前の章と同じ仕組み) |
集合や辞書のキーにしたいなら frozen=True |
__hash__ が作られ、同じ中身は1件にまとまる |
大小比較は order=True |
フィールドを定義順に並べたタプルとして比べる |
eq=False は「同じオブジェクトか」で比べる |
中身が同じでも False |
利用者向けの表示は __str__ を自分で足す |
Money の例のように、print と一覧表示を分けられる |
「データを入れるだけのクラスなら dataclass に任せ、表示を整えたいときだけ __str__ を足す」と、手で書く量を大きく減らせます。
つまずきやすい点と確認リスト
| 現象 | 原因 | 対応 |
|---|---|---|
print(リスト) で <__main__.X object at 0x...> が並ぶ |
__repr__ が無い(__str__ だけ定義している) |
__repr__ を定義する |
"文字" + obj で TypeError |
+ では自動で文字列にならない |
str(obj) か f文字列を使う |
f"{obj:>10}" で TypeError |
既定の __format__ は書式指定を受け付けない |
f"{obj!s:>10}" |
__eq__ を足したら集合に入らなくなった |
__hash__ が None になった |
__hash__ も定義する、または frozen な dataclass にする |
== が中身を比べてくれない |
__eq__ が無い |
__eq__ を定義する、または dataclass にする |
__str__ が TypeError: __str__ returned non-string |
文字列以外を返している | f文字列や str() で文字列にして返す |
ログに __str__ の表示しか出ず、中身がわからない |
%s で渡している |
確認用のログは %r や f"{obj!r}" |
書き終えたら見直したい項目です。
| 確認すること | 目安 |
|---|---|
__repr__ があるか |
クラス名(属性=値, ...) の形で、属性に !r を付けている |
__str__ は必要か |
利用者向けの見た目が __repr__ と違うときだけ書く |
__eq__ と __hash__ がそろっているか |
同じ属性を使っている |
別の型との比較で NotImplemented を返しているか |
isinstance で確かめてから比べる |
| dataclass で済むか | __init__・__repr__・__eq__ を手で書いているなら置き換えを考える |
よくある質問
Q. __str__ と __repr__ はどちらを先に書けばよいですか?
A. __repr__ です。__str__ が無いときは print も __repr__ を使うので、1つ書くだけで print・リストの中・対話シェル・デバッガのすべてが読める表示になります。利用者向けに別の見た目が必要になったら、そのときに __str__ を足します。
Q. print では読めるのに、リストに入れて print すると <__main__.X object at 0x...> になります。
A. __str__ だけを定義しているためです。リストや辞書は、要素を表示するときに __repr__ を呼びます。__repr__ を定義すれば、リストの中でも読める表示になります。要素を __str__ の形で並べたいときは ", ".join(map(str, リスト)) を使います。
Q. f文字列の !r は何をしていますか?
A. 値を repr() で文字列にしてから埋め込みます。f"{b!r}" は f"{repr(b)}" と同じ結果です。文字列に引用符が付くので、空文字や前後の空白がはっきり見えます。f"{b=}" と書くと「変数名=repr の結果」の形で表示され、確認用の出力に便利です。
Q. dataclass にすれば __str__ も作られますか?
A. 作られません。今回の確認では、@dataclass が作ったのは __init__・__repr__・__eq__ などで、__str__ は object のもの(__repr__ を呼ぶ)がそのまま使われていました。利用者向けの表示が必要なら、dataclass の中に __str__ を自分で書き足します。
まとめ
__str__は人に見せる表示、__repr__は開発者が中身を確かめる表示。print・str()・f文字列は__str__、repr()・対話シェル・!r・コンテナの中は__repr____str__が無ければ__repr__が使われる。1つだけ書くなら__repr__- リストや辞書の中は必ず
__repr__。<__main__.X object at 0x...>が並ぶときは__repr__を足す __repr__はクラス名(属性=値)の形にし、属性には!rを付ける__eq__を定義すると__hash__がNoneになる。集合や辞書のキーに使うなら__hash__もそろえる- 特殊メソッドは「決まった書き方をしたときにPythonが呼ぶ」 。エラー文から足すメソッドを逆引きできる
- dataclass は
__init__・__repr__・__eq__を自動で作る。__str__は作られないので必要なら自分で足す
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python __str__と__repr__の違い|特殊メソッド一覧」
株式会社RJCでは、Pythonの特殊メソッドやクラスの書き方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。