naritoブログ

【お知らせ】
新ブログができました。今後そちらで更新し、このサイトは更新されません(ウェブサイト自体は残しておきます)
このブログの内容に関してコメントしたい場合は、新ブログのフリースペースに書き込んでください

このブログの内容を新ブログに移行中です。このブログで見つからない記事は、新ブログにありま

Python、対話モードでの自動表示とprintの違い

約944日前 2018年3月22日8:17
プログラミング関連
Python
対話モード(インタラクティブシェル)では、式を書くと結果オブジェクトが自動で表示されます。

対話モードというのは、この画面ですね。
対話モード


式のおさらい


式を書くと結果オブジェクトが自動表示というのは、以下のことです。1とか'Hello'とか、勝手に表示されていますね?Noneは結果の自動表示がないのです、実は。

>>> 1
1
>>> a = 1
>>> a
1
>>> 'Hello'
'Hello'
>>> None
>>>



式というのは、文ではないものと考えましょう。if、for、while文は文として解りやすい例ですね。
代入も文ですが、代入文の右辺に書くものは必ず式なので、代入の右辺におけるものと考えると解りやすいかもしれないですね。この右辺に書いているものを直接書くと、結果が自動表示されます。

>>> a = 1
>>> a = 1 + 1
>>> a = [x for x in range(10)]
>>> a = lambda x: x**2
>>> a = str(1)



その自動表示された値と、printで表示した値の違いは何か?というのが本題です。

>>> age = 28
>>> age
28 # 自動表示された値
>>> print(age)
28 # printで表示した値



__repr__と__str__


結論から説明すると、式を単体で書いた場合は結果オブジェクトの__repr__が、print関数では__str__が呼び出されます。試してみますと、確かに__repr__と__str__がそれぞれ呼び出されます。

>>> class Test:
... def __repr__(self):
... return 'repr'
... def __str__(self):
... return 'str'
...
>>> tes = Test()
>>> tes
repr
>>> print(tes)
str




Testクラスは私が作ったクラスですが、Pythonの組み込み型でも同じ挙動です。確認してみます。
まず、文字列をprintするとHello、結果の自動表示で'Hello'ですね。

>>> a = 'Hello'
>>> print(a)
Hello
>>> a
'Hello'


__str__や__repr__を明示的に呼び出し、上と同じ結果になることを確認します。(今回は特殊メソッドを直接読んでいますが、一般的には組み込み関数str、reprを使います)

>>> a_str = a.__str__()
>>> a_repr = a.__repr__()
>>> print(a_str)
Hello
>>> print(a_repr)
'Hello'
>>>
>>> a_repr
"'Hello'"


__str__の結果を表示すると、「Hello」、__repr__の結果を表示すると「'Hello'」とシングルクォート付きで出力されました。print(a)と単体でのaを出力した場合と同じですね。

2点疑問が出る方もいるかもしれません。
1. print(a_str)やprint(a_repr)でprint関数を使っているが、これは何かおかしいのではないか、__str__が呼ばれるのではないか。
2. 最後の>>> a_reprが「"'Hello'"」となっているのはなぜか。

先に2番目の疑問にお答えすると、a.__repr__()の結果として「'Hello'」という文字列(シングルクォート含む)が返されたのですが、「>> a_repr」とすることで文字列'Hello'(シングルクォート含む)の__repr__が呼び出されたためです。文字列の__repr__を呼び出すと、それが文字列であるのを示すために、シングルクォートやダブルクォートで囲まれるのです。

>>> a = 'Hello'
>>> a = repr(a)
>>> a = repr(a)
>>> a = repr(a)
>>> a = repr(a)
>>> a = repr(a)
>>> a
'\'\\\'\\\\\\\'"\\\\\\\\\\\\\\\'Hello\\\\\\\\\\\\\\\'"\\\\\\\'\\\'\''



printでは__str__の結果を出力しますが、文字列は何度__str__を呼び出しても同じ値の文字列です。ですので、__repr__で返された文字列ならば、printで確認しても問題はありません。むしろ、__repr__で返された文字列に対して__repr__を呼び出すようなことをすると、上の例のように妙な表示になります。

>>> a = 'Hello'
>>> a = str(a)
>>> a = str(a)
>>> a = str(a)
>>> a = str(a)
>>> a = str(a)
>>> a
'Hello'




なぜ_str__と__repr__の結果が違うのかといいますと、__str__は人が見やすい、簡単な形でオブジェクトの文字列表現を返すのに対して、__repr__は主に開発者向けで、デバッグ時等によく使われます。
よくやるのは、evalで同じ形に復元できるようにreprを実装したり...

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __repr__(self):
# 汎用的なクラス名の取得。self.__class__.__name__もよく見る
# このように取得しておくと、サブクラス化してもreprをそのまま使えることがあります
class_name = type(self).__name__

#{!r}とすることで、'1'と1の区別が付けられます。
return '{}({!r}, {!r})'.format(class_name, self.name, self.age)

person = Person('narito', 28)
print(repr(person))




Person('narito', 28)



又は、以下のような形式もよく見ます。<...some useful description...>という形ですね。

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __repr__(self):
return '<Person {} {}>'.format(self.name, self.age)

person = Person('narito', 28)
print(repr(person))




<Person narito 28>



Requestsなんかでは、HTTPレクエストを送った後に返されるHTTPレスポンスのオブジェクトをあっさりと表現しており、私は好きです。属性が多すぎるオブジェクトでは、全ての属性を返す訳にもいきません。そもそも開発者がどんな情報を求めるのか?というのも重要な点ですね。

>>> import requests
>>> res = requests.get('https://torina.top/')
>>> res
<Response [200]>



先程のPersonクラスに__str__を実装するならば、以下のような感じでも良いでしょう。__str__はカジュアルです。

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __str__(self):
return '{}さん、{}歳'.format(self.name, self.age)

person = Person('narito', 28)
print(person)




naritoさん、28歳





repr() 組み込み関数によって呼び出され、オブジェクトを表す「公式の (official)」文字列を計算します。可能なら、これは (適切な環境が与えられれば) 同じ値のオブジェクトを再生成するのに使える、有効な Python 式のようなものであるべきです。できないなら、 <...some useful description...> 形式の文字列が返されるべきです。戻り値は文字列オブジェクトでなければなりません。クラスが __repr__() を定義していて __str__() は定義していなければ、そのクラスのインスタンスの「非公式の (informal)」文字列表現が要求されたときにも __repr__() が使われます。

公式ドキュメント __repr__について




オブジェクトの「非公式の (informal)」あるいは表示に適した文字列表現を計算するために、 str(object) と組み込み関数 format(), print() によって呼ばれます。戻り値は string オブジェクトでなければなりません。

__str__() が有効な Python 表現を返すことが期待されないという点で、このメソッドは object.__repr__() とは異なります: より便利な、または簡潔な表現を使用することができます。

組み込み型 object によって定義されたデフォルト実装は、 object.__repr__() を呼び出します。

公式ドキュメント __str__について