概要
Effective Pythonを読んでいると以下のようなコードが。
class Tool:
def __init__(self, name, weight):
self.name = name
self.weight = weight
def __repr__(self):
return f'Tool({self.name!r}, {self.weight})'
f文字列自体は日頃から利用していますし、reprメソッドについては「strメソッドよりオフィシャルな返り値で、strメソッドがなかったら代わりに呼び出されたりする」程度の認識はあります。(参考サイト: Python-特殊メソッド: __repr__と__str__)
ですが、そのf文字列中で利用されている!rについては全くの初見でした。エクスクラメーションマークは特殊文字扱いなのかGoogleで検索しても上手くヒットしませんし、正式な名称も知らないので無駄に手こずる有様。
ここでは同じような疑問にぶち当たった方のために、メモがてら記事を残しておきます。
結論
ざっくり言ってしまうと、f文字列中の!は、文字列内の{}のところを評価するために呼び出すメソッドを決定するためのものです。公式サイトには以下のように表記があります。
If a conversion is specified, the result of evaluating the expression is converted before formatting. Conversion '!s' calls str() on the result, '!r' calls repr(), and '!a' calls ascii().
和訳するとこんな感じでしょうか。
変換が指定されると、表現を評価した結果がフォーマッティングの前に変換されます。!sではstrメソッドが、!rではreprメソッドが、!aではasciiメソッドが呼ばれます。
簡単なサンプルを作るとこんな感じ?
class Sample:
def __init__(self):
pass
def __repr__(self):
return "__repr__ method is called."
def __str__(self):
return "__str__ method is called."
def __ascii__(self):
return "__ascii__ method is called."
sample = Sample()
print(f"{sample!r}")
print(f"{sample!s}")
print(f"{sample!a}")
__repr__ method is called.
__str__ method is called.
__repr__ method is called.
ちなみに、何も指定していないとstrメソッドが呼ばれるようになっています。
print(f"{sample}")
__str__ method is called.
まとめ
ということで、変換フラグについての理解はある程度深まりました。冒頭のコードで用いられている!rは、strクラスに実装されている__repr__メソッドを呼び出すための変換フラグということになります。
上と同じ様に3種の変換フラグを使ってf文字列をprintすると、以下のようになります。
sample_text = "sample"
print(f"{sample_text!r}")
print(f"{sample_text!s}")
print(f"{sample_text!a}")
print(f"{sample_text}")
'sample'
sample
'sample'
sample
……正直、'がついているかいないかくらいの違いで大した差はないんじゃない?と思ってしまいます。が、よくよく考えると、文字列であることをきちんと明示させるためには、__repr__メソッドを使うのが賢い気もします。
ここまで読んでくださってありがとうございます。誤りに気づかれたり、あるいは「こんな利点、利用方法があるよ」という知見をお持ちの方は、ぜひコメントにて情報提供していただけると幸いです。
集合知に感謝。