__str__()と__repr__()はどちらもオブジェクトの文字列表現を返すために利用される特殊メソッドです。
これらのメソッドはobjectクラスで定義されています。すべてのクラスは暗黙にobjectクラスを継承しますので、独自に作成したクラスにもこれらのメソッドが継承されます。独自クラスでは適切な文字列を返すように、これらのメソッドをオーバーライドすべきものです。
しかし、これらのメソッドを具体的にどのようにオーバーライドしたら良いかわかりづらいのも事実です。この記事では独自クラスでの__str__()と__repr__()の使い方や正しい実装方法をできるよう詳しく解説していきます。
str()とrepr()は__str__()と__repr__()を呼び出す
通常str()とrepr()はそれぞれ__str__()と__repr__()を呼び出し、それらが返す文字列をそのまま返します。そのためstr()とrepr()が返す文字列を理解することは役に立ちます。
str()やrepr()はどちらもオブジェクトの文字列表現を返しますが、具体的には次のような文字列を返します。
- str()
-
人間が見て分かりやす形式でオブジェクトを表した文字列を返す。
- repr()
-
repr()が返す文字列は、適切な環境でeval()に渡したときに同じ値のオブジェクトを生成できる文字列です。そのような文字列を返すことが不可能なら、山括弧(<>)に囲まれたオブジェクトの型の名前と追加の情報(大抵はアドレスやID)を返す。
実際にこれらのメソッドがどのような文字列を返すか、標準ライブラリのFractionオブジェクトと独自に作成したオブジェクトで見てみましょう。
str()とrepr()には引数としてオブジェクト(の参照)を渡します。するとそのオブジェクトの__str__()と__repr__()が暗黙に呼び出されます。
>>> from fractions import Fraction
>>> f = Fraction(3, 7)
>>> str(f)
'3/7'
>>> repr(f)
'Fraction(3, 7)'
>>> print(f)
3/7
Fractionオブジェクトのstr()やprint()が返す文字列は慣れ親しんだ数学の記法で人間にわかりやすい形式です。repr()が返す文字列は最初にオブジェクトを生成したときの式そのものです。これをeval()に渡すと同じ値のFractionオブジェクトを再生成できます。
次に独自のPointクラスを定義してどのような結果になるか見てみましょう。Pointオブジェクトは直交座標系の1点を表し、x座標とy座標を持ちます。
>>> class Point:
... def __init__(self, x, y):
... self.x = x
... self.y = y
...
>>> p = Point(3, 7)
>>> str(p)
'<__main__.Point object at 0x10b5d8a90>'
>>> repr(p)
'<__main__.Point object at 0x10b5d8a90>'
>>> type(p)
<class '__main__.Point'>
>>> id(p)
4485646992
>>> 0x10b5d8a90
4485646992
最初にPointクラスを定義し、このクラスのオブジェクトを生成しています。そして生成したオブジェクトに対してstr()とrepr()をそれぞれ呼び出しています。
結果はどちらも型名(クラス名)、idその他を山括弧で囲んだものです。
str()とrepr()は__str__()と__repr__()を呼び出しますが、Pointクラスで__str__()と__repr__()をオーバーライドしていないので、objectクラスで定義されたデフォルトの実装を呼び出します。デフォルトの実装は上記のような山括弧で囲まれた文字列を返します。
この例から分かる通りPointオブジェクトのstr()とrepr()は前述のような文字列を返せていません。objectクラスではどのようなクラスに継承されるか分かりませんので、サブクラスに適切な__str__()と__repr__()を提供できません。__str__()と__repr__()が適切な文字列を返すように実装するのはサブクラスの責任なのです。言い換えると、新しく定義したクラスでは適切な文字列を返すように__str__()と__repr__()をオーバーライドすべきです。
新しく定義したクラスでどのように__str__()と__repr__()を実装するかは後ほど説明します。
str()とrepr()が返す文字列の例
Pythonの組み込みクラスやライブラリクラスのオブジェクトに対してstr()とrepr()を呼び出したとき、どのような文字列を返すかもう少し例を見てみましょう。これは__str__()や__repr__()を自分で実装するときの参考になります。
初めに組み込みのオブジェクトから見てみましょう。
>>> str(5)
'5'
>>> repr(5)
'5'
>>> str(['a', 'b', 'c'])
"['a', 'b', 'c']"
>>> repr(['a', 'b', 'c'])
"['a', 'b', 'c']"
整数やリストに対して、str()とrepr()はどちらも同じ文字列を返します。
これらの文字列はコードに記述する形式そのものです。コードに記述する形式は、当然人間が見て分かりやすいのでstr()が返す文字列として適切です。またこれらの文字列はオブジェクトを生成する式そのものですのでrepr()が返す文字列として適切です。
Fractionクラスは先ほど見ました。ここでは標準ライブラリで定義されているその他のクラスについてもいくつか見てみましょう。
>>> from ipaddress import IPv4Network
>>> str(IPv4Network('192.168.1.0/24'))
'192.168.1.0/24'
>>> repr(IPv4Network('192.168.1.0/24'))
"IPv4Network('192.168.1.0/24')"
>>> from datetime import datetime
>>> str(datetime(2023, 3, 4, 10, 45, 28))
'2023-03-04 10:45:28'
>>> repr(datetime(2023, 3, 4, 10, 45, 28))
'datetime.datetime(2023, 3, 4, 10, 45, 28)'
それぞれのオブジェクトに対するstr()がどのような文字列を返すか見ていきましょう。
datetimeオブジェクトは日時を表していることが一目でわかる文字列を返します。IPv4Networkオブジェクトもネットワークアドレスとサブネットマスク表す標準的な記法の文字列を返します。
このようにstr()は人間に分かりやすくオブジェクトを適切に表した簡潔な文字列を返します。ただし、特に決まった形式はなく、オブジェクトによって人間にわかりやすい表現が選ばれています。
次のrepr()が返す文字列について見てみましょう。
どちらもオブジェクトを生成する式ですが、datetimeに対するrepr()が返す文字列はモジュール名(datetime)が余分についてます。Pythonのライブラリでもrepr()が返す文字列に一貫性が欠けていそうです。
__str__()と__repr__()が返す文字列
str()とrepr()はそれぞれ__str__()と__repr__()が返す文字列を通常そのまま返すので、__str__()と__repr__()の公式ドキュメントもこれまで説明してきた「str()とrepr()が返す文字列」と同じような記載なのですが、追加で以下のような記載もあります。
- __str__()はオブジェクトの非公式(informal)な文字列表現を返す。
- __repr__()はオブジェクトの公式(offical)な文字列表現を返す。
「非公式な文字列表現」と「公式な文字列表現」という言い回しは分かりづらいですね。これらの文言が何を意味するか掘り下げて見ましょう。
非公式(informal)と公式(offical)とは?
「非公式」とは噛み砕いていうと特に決まりがない、少し乱暴な言い方をするとなんでも良いということです。
「公式」とは反対に決まりがあるということです。どのような決まりかというとrepr()が返す文字列で説明したものそのものです。
つまり__repr__()が返す「オブジェクトの公式な文字列表現」とは、決まりにしたがった形式でオブジェクトを表した文字列ということになります。その決まりとは次の2つのです。
そしてその文字列は有益で簡潔にオブジェクトを表しているべきものです。
また通常オブジェクトはオブジェクトごとに独自のデータを持っていますので、オブジェクトを表すためにこれらを含めることになるでしょう。
「公式」というのは決まりあるということですが、それは前述したrepr()が返す文字列の説明そのものです。
__str__()と__repr__()が返す文字列のまとめ
これまで説明してきた話をまとめると__repr__が返すべき文字列は次のものです。
- 人間が理解しやすい形でオブジェクトを表した文字列
- しかし、どのような文字列か特に決まりはない。オブジェクトごとに分かりやすい表現を選べば良い。
- オブジェクト通常、それぞれ固有のデータを持っているのでそれを文字列に含める。
3つ目についてはこれまでに具体的な説明にありませんでしが、str()が返す文字列の例から分かる通りオブジェクト固有のデータはオブジェクトを雄弁に表しているので、通常これを含めることになるでしょう。
__repr__()が返す文字列のまとめは次のとおりです。
- オブジェクトを表す文字列
- どのような文字列にするか決まりがあり、それは次のとおりです。
- 可能であれば(適切な環境が与えられたとき)同じ値のオブジェクトを再生成するのに使用できるPython式のようなものであるべき
- それができなければ オブジェクトの型の名前と追加の情報(大抵はアドレスやID)を山括弧(<>)で囲んだ文字列
これはrepr()が返す文字列として説明した内容とほとんど同じです。
独自クラスで__str__()と__repr__()を実装する
__str__()と__repr__()メソッドがどのような文字列を返すべきかという抽象的な話はこれまでに説明してきました。ここでは締めくくりとして独自クラスで実際に__str__()と__repr__()メソッドを実装する例を見て見ましょう。
先ほどPointクラスを定義しましがが、このクラスで__str__()と__repr__()をオーバーライドして見ましょう。
>>> 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'{self.__class__.__name__}({self.x}, {self.y})'
...
>>> p = Point(3, 7)
>>> str(p)
'(3, 7)'
>>> repr(p)
'Point(3, 7)'
Pointオブジェクトは直交座標系の1点をx座標とy座標で表しますので、__str__()は「(x, y)」の形式の文字列を返すように実装します。
__repr__()はオブジェクトを生成したときの式を文字列として返すよう実装します。クラス名を取得するために__class__.__name__を使用しています。
このように__repr__()はオブジェクトを生成する式を提供できるのが普通ですが、もしできなければ山括弧(<>)で囲んだ文字列を返す必要があります。これはobjectクラスで定義された__repr__()の実装そのものですので、単にサブクラスで__repr__()をオーバーライドしないようにします。
まとめ
__str__()と__repr__()メソッドが返す文字列はデバッグなどによく使われます。そのため簡潔で分かりやす文字列を返すように心がけましょう。