print()で変数の中身を表示したら、10と出てきた。ところが同じ値をリストに入れて表示すると、今度は'10'とクォート付きで出てくる。そんな場面に出会って、首をかしげたことはありませんか?
じつはPythonには、値を文字列にする方法が大きく2種類あります。人に読んでもらうための表示と、開発者が中身を正確に確かめるための表示です。
この2つを担当しているのが、str()とrepr()という組み込み関数です。名前は地味ですが、デバッグの効率を大きく左右する存在です。
この記事では、str()とrepr()の違いを、実際に動かせるコードで一歩ずつ確かめていきます。サンプルの実行結果は、手元のPython 3.11で確認したものです。
str()とrepr()をひと言でいうと¶
まずは、2つの関数の役割をざっくり押さえておきましょう。たとえ話を使うと、違いがぐっとイメージしやすくなります。
str()は、お店のショーケースに並べる値札のようなものです。お客さんが読みやすいように、余計な情報をそぎ落として見せてくれます。
repr()は、倉庫の在庫管理表に書く記録です。読みやすさよりも、それが何で、どんな中身なのかを正確に残すことを優先します。
公式ドキュメントでも、__str__は非公式で読みやすい表現、__repr__は公式の表現と説明されています。repr()の結果は、できる限りそのオブジェクトを作り直せるPythonの式に近い形にすることが勧められています。
2つの違いを表にまとめると、次のようになります。
| 観点 | str() | repr() |
|---|---|---|
| たとえ | ショーケースの値札 | 倉庫の在庫管理表 |
| 主な読み手 | アプリの利用者 | 開発者 |
| 呼ばれる特殊メソッド | __str__ |
__repr__ |
| 文字列'10'の表示 | 10 | '10' |
| 使われる場面 | print()、format()、f-string | 対話モードの表示、リストの中身、デバッグ |
| 目指す形 | 簡潔で読みやすい | 正確で曖昧さがない |
実際に表示を比べてみよう¶
言葉だけではピンとこないので、さっそく手を動かしてみましょう。次のコードは、いくつかの値をstr()とrepr()の両方で表示する例です。
import datetime
values = ["10", 10, None, datetime.date(2026, 10, 30)]
for v in values:
print(str(v), "|", repr(v))
# 10 | '10'
# 10 | 10
# None | None
# 2026-10-30 | datetime.date(2026, 10, 30)
数値の10やNoneは、どちらで表示しても同じです。違いがはっきり出るのは、文字列と日付の行です。
文字列の"10"は、str()だとただの10ですが、repr()だと'10'とクォートが付きます。これで、数値の10なのか文字列の"10"なのかを見分けられるわけです。
日付も同様に、str()は2026-10-30という人間向けの書き方です。repr()はdatetime.date(2026, 10, 30)と、そのまま貼り付ければ同じ日付を作れる形になっています。
リストに入れると表示が変わる理由¶
冒頭で触れた、リストに入れるとクォートが付く現象の正体もここにあります。リストを文字列にするとき、Pythonは中の要素をrepr()で表示するからです。
次のコードで確かめてみましょう。文字列の"10"と数値の10を、同じリストに入れています。
print("10") # 10
print(10) # 10
print(["10", 10]) # ['10', 10]
単独でprint()すると、どちらも10と表示されて区別がつきません。けれどもリストに入れると、文字列のほうだけクォート付きになります。
辞書やタプルでも同じことが起きます。入れ物の中身は正確さが大事なので、repr()が選ばれているのだと考えると覚えやすいでしょう。
ちなみに、対話モードで変数名だけを打ち込んだときに出てくる表示もrepr()です。print()を通したときと見た目が違うのは、このためです。
print()の引数の使い方を整理したい方は、こちらの記事もどうぞ。
【関連記事】Pythonのprint()を使いこなす。sep・end・file・flushの4つの引数を初心者向けに解説
自作クラスで__str__と__repr__を書き分ける¶
自分でクラスを作ったとき、何も設定しないとどう表示されるのでしょうか。試しに、中身が空のクラスを表示してみます。
class Product:
pass
print(Product())
# <__main__.Product object at 0x7f342a302250>
クラス名とメモリ上の住所のような数字しか出てきません。これでは、中にどんな商品が入っているのかまったくわかりませんね。
表示を整えるには、__str__と__repr__という特殊メソッドを定義します。特殊メソッドとは、名前の前後にアンダースコアが2つずつ付いた、Pythonが決まった場面で自動的に呼び出すメソッドのことです。
まずは__repr__から書く¶
最初に書くべきなのは__repr__のほうです。次のコードでは、商品名と価格を持つクラスに__repr__だけを定義しています。
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __repr__(self):
return f"Product(name={self.name!r}, price={self.price!r})"
apple = Product("りんご", 120)
print(apple) # Product(name='りんご', price=120)
print([apple]) # [Product(name='りんご', price=120)]
__str__を書いていないのに、print()でもきれいに表示されました。公式ドキュメントにあるとおり、__str__がないクラスでは、代わりに__repr__が使われるからです。
つまり、__repr__を1つ書いておくだけで、print()でもリストの中でも中身が見えるようになります。どちらか片方だけ書くなら、先に書くべきは__repr__と覚えておきましょう。
コードの中の!rは、f-stringで値をrepr()に通す指定です。これを付けておくと、文字列の名前にはクォートが付き、数値はそのまま表示されます。
利用者向けの表示が必要なら__str__を足す¶
画面やメールで商品を見せたい場合は、__str__も追加します。次のコードは、さきほどのクラスに利用者向けの表示を足した例です。
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __repr__(self):
return f"Product(name={self.name!r}, price={self.price!r})"
def __str__(self):
return f"{self.name}({self.price}円)"
orange = Product("みかん", 80)
print(orange) # みかん(80円)
print(f"{orange}") # みかん(80円)
print([orange]) # [Product(name='みかん', price=80)]
print()やf-stringでは、利用者向けのみかん(80円)が表示されます。一方でリストの中に入れると、開発者向けの表示に切り替わりました。見せる相手によって自動で使い分けてくれるわけです。
特殊メソッド全体の仕組みについては、こちらの記事で詳しく紹介しています。
【関連記事】Pythonの特殊メソッドとは?どんな種類があるのか?どうやって使うのか?
dataclassなら__repr__を自動で作ってくれる¶
毎回__repr__を手で書くのは、正直なところ少し面倒です。そんなときは、標準ライブラリのdataclassを使うと楽になります。
次のコードは、デコレータを1行付けただけのクラスを表示した例です。
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
print(User("佐藤", 30)) # User(name='佐藤', age=30)
__repr__を書いていないのに、クラス名と各属性がきちんと表示されています。dataclassが、さきほど手で書いたのと同じような__repr__を自動で用意してくれるからです。
【関連記事】Pythonのdataclassとは?クラスの定義がぐっと短くなる書き方を初心者向けに解説
f-stringの!rと=でデバッグを速くする¶
ここからは、日々のデバッグで役立つ使い方を見ていきます。f-stringには、repr()を手軽に呼び出す仕組みが2つ用意されています。
1つ目は、さきほども登場した!rです。公式ドキュメントでは、!sがstr()を、!rがrepr()を、!aがascii()を呼ぶ変換指定として説明されています。
2つ目は、Python 3.8で追加された=の指定です。変数名のあとに=を付けると、式と値をまとめて表示してくれます。
次のコードで、2つの書き方を比べてみましょう。
x = "a b\t"
print(f"{x}") # a b ← タブが空白に見える
print(f"{x!r}") # 'a b\t'
print(f"{x=}") # x='a b\t'
ふつうに表示すると、末尾のタブはただの空白にしか見えません。!rや=なら、\tとしてはっきりわかります。
=を付けたときは、何も指定しなければrepr()の形で表示されます。どの変数の値なのかも一緒に出るので、print()デバッグがとても読みやすくなります。
f-stringの基本や、ほかの書式指定について知りたい方はこちらもあわせてどうぞ。
【関連記事】Pythonのf-stringとは?デバッグが驚くほど楽になる!
見えない空白や全角スペースを見抜く¶
repr()がいちばん頼りになるのは、目に見えない文字がからむバグです。たとえばCSVやフォームから受け取った文字列に、全角スペースが紛れ込んでいるケースを考えてみましょう。
次のコードでは、全角スペースを含む文字列をprint()とrepr()で表示しています。
name = "山田 太郎"
print(name) # 山田 太郎
print(repr(name)) # '山田 太郎'
print(name == "山田 太郎") # False
print()で見ると、半角スペースなのか全角スペースなのか見分けがつきません。repr()なら、全角スペースが という番号で表示されるので一目瞭然です。
同じように、文字列の末尾に紛れ込んだ改行やタブも、repr()なら\nや\tとして浮かび上がります。比較がなぜかFalseになるときは、まず両方の値をrepr()で表示してみるのがおすすめです。
私は10年ほどエンジニアとして開発に関わってきましたが、顧客データの照合が合わない原因が、末尾の改行1文字だったことが何度もあります。ログにrepr()の形で値を出すようにしてからは、こうした調査にかかる時間が目に見えて減りました。
全角と半角が混ざった文字列を、そもそもそろえておきたい場合はこちらの記事が参考になります。
【関連記事】Pythonで全角と半角をそろえるには?unicodedata.normalize(NFKC)の使い方と、①や㈱まで変わってしまう落とし穴を初心者向けに解説
よくある落とし穴と注意点¶
便利なrepr()ですが、使い方を誤ると思わぬところでつまずきます。ここでは、初心者がはまりやすいポイントを表で整理しておきます。
| 落とし穴 | 何が起きるか | 対策 |
|---|---|---|
| 画面表示にrepr()を使う | 利用者にクォートや が見えてしまう | 利用者向けにはstr()を使う |
__str__だけ書く |
リストの中やデバッグ時は住所表示のまま | 先に__repr__を書く |
__repr__で文字列以外を返す |
TypeErrorになる | 必ず文字列をreturnする |
| 巨大なリストをrepr()する | ログが何万文字にもなる | 標準ライブラリのreprlib.repr()で省略表示する |
| パスワードを属性に持つ | ログに秘密の値が残る | __repr__から除外する |
なかでも気をつけたいのが、最後の秘密情報の扱いです。__repr__で全属性を表示していると、例外のメッセージやログ経由で、パスワードやトークンがそのまま記録されてしまいます。
dataclassを使う場合は、field(repr=False)を指定すると、その属性を表示から外せます。手書きの__repr__なら、最初から含めないようにしておきましょう。
実務でのrepr()の使いどころ¶
最後に、実際の開発でrepr()をどう活かすかを整理しておきます。基本の考え方は、人に見せる場所ではstr()、自分たちが確かめる場所ではrepr()です。
ログ出力では、値の前後に余計な空白や改行がないかを確かめられるよう、!rを付けておくと安心です。エラーメッセージに入力値を含めるときも、repr()の形にしておくと、空文字と半角スペースの区別がつきます。
以前、外部APIから届いた値がたまに一致しない不具合を追いかけたことがあります。print()の結果はまったく同じに見えたのに、repr()で出してみたら片方だけ文字列の"1"で、もう片方は数値の1でした。それ以来、値が怪しいときはまずrepr()を合言葉にしています。
まとめ¶
今回は、str()とrepr()の違いを、サンプルコードを動かしながら見てきました。最後にポイントを振り返っておきましょう。
str()は利用者に見せるための読みやすい表示で、repr()は開発者が中身を正確に確かめるための表示です。リストの中身や対話モードの表示がクォート付きになるのは、repr()が使われているからです。
自作クラスでは、まず__repr__を書いておけば、__str__がなくてもprint()で中身が見えます。利用者向けの表示が必要になったら、そこで__str__を足せば十分です。
デバッグでは、f-stringの!rや=を使うと、見えない空白や型の違いがすぐにわかります。比較がFalseになったらrepr()で確かめる、この一手を覚えておくだけで、調査の時間はぐっと短くなるはずです。
まずは手元で、いつもprint()している変数をrepr()でも表示してみてください。
ここまでお読みいただきありがとうございました。