これは Python 3.12 時点での話
みんな知ってそうなことは書きません。
最初の方では !r, !s, = 指定子、そのあとに文字列と新機能の話。
基本
from datetime import datetime
animals = [
"dog", "goat", "wombat", "cat",
]
# 大体のものは自然に文字列になる
print(f"animals: {animals}")
# animals: ['dog', 'goat', 'wombat', 'cat']
# 関数、演算、リスト内包表記...式の中で評価して文字列にしてくれる
print(f"now: {datetime.now()}")
# now: 2024-06-07 02:31:03.078175
print(f"{[2**n for n in range(1, 12)]}")
# [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]
ちゃんと知ってる?な話
指定子 !r
と !s
は repr と str
そもそも、あるオブジェクトが print や f-strings に評価されるとき、何をもって文字列になるのでしょうか。
JavaだったらtoString()
ですが、Pythonだとそれは__str__()
です。
似た存在として__repr__()
もあります。
-
__str__
:ユーザが見て理解しやすい文字列- → f-strings の
!s
指定子で呼ばれる - → print()で呼ばれる
- → f-strings の
-
__repr__
:デバグに使う、それを見て再現できるような文字列- → f-strings の
!r
指定子で呼ばれる - → repr()で呼ばれる
- → f-strings の
たとえば datetime でいうと、こうなっています。
__str__
の方が見やすくて、__repr__
の方が開発者的かと思います。
こう作るものです。
f-string内の式でオブジェクトが評価されるときデフォルトで
__str__()
なので、!s
は書く意味がありません
# __str__() 略して 's'
print(f"{now!s}")
# 2024-06-07 02:31:03.078175
# __repr__() 略して 'r'
print(f"{now!r}")
# datetime.datetime(2024, 6, 7, 2, 31, 3, 78175)
# 通常呼ばれるのは's'の方
print(f"{now}")
# 2024-06-07 02:31:03.078175
指定子 =
デバグ用でいうと、=
指定子もあります。
その変数のその時点での値を示すためのもの。
これは、その変数名 = __repr__()
みたいな文字列になるものです。
変数名とその実体の__repr__
がちゃんとしていれば、これがかなり強力。
print("status=", f"{status!r}")
# これは同じ出力
print(f"{status= }")
!r, !s, = 共存
指定子は共存することができます。
実際に実装して試してみると、こうなります。
この使い分けを意識してデバグしたり開発したりしたいです
f"{person!s}"
とf"{person=!r}"
は頭痛が痛い的な感じ
class Person():
def __repr__(self) -> str:
return "__repr__"
def __str__(self) -> str:
return "__str__"
person = Person()
print(f"{person}")
# __str__
print(f"{person!s}")
# __str__
print(f"{person!r}")
# __repr__
print(f"{person=}")
# person=__repr__
print(f"{person=!s}")
# person=__str__
print(f"{person=!r}")
# person=__repr__
# `f"{person!s}"`と`f"{person=!r}"` は意味がない!
f-strings の実用的な例
ここからは実用的な例
!r, !s, = の実用的な例
- =指定していれば、変数名がその文字列の意味を教えてくれる
- reprはそれで再現可能な文字列 → 大体クラス名は出す
つまりこれからはType()を使う機会が減るかも。
animals = ["dog", "goat", "wombat", "cat",]
try:
animals[20]
except Exception as err:
# ✕ これは冗長
print(f"err: {err}, type: {type(err)}")
# err: list index out of range, type: <class 'IndexError'>
# ✕ これはraiseなのかcatchなのか分からない
print(f"{err!r}")
# IndexError('list index out of range')
# 〇 `=`を使えば意味も型名も分かる
print(f"{err=}")
# err=IndexError('list index out of range')
こんなこともできる
リストを縦に並べて中央揃え
# Python 3.12 から式の中でバックスラッシュが使えるようになった
print(f"{'\n'.join(animals): ^12}")
# dog
# goat
# wombat
# cat
dict要素をわざわざ変数に逃がす必要はない
# Python 3.12 からダブルクオートの入れ子ができるようになった
dic = {
"dog": "犬",
"goat": "ヤギ",
}
print(f"goat: {dic["goat"]}")
# goat: ヤギ
改行もコメントも書ける
# '{}'内は構文中なので改行もコメントもできる
print(f"First animal is: {
animals[0] # コメントが書けるし
# 改行もできる
}.")
# First animal is: dog.
入れ子
# f-strings の再利用が可能
txt = "いい例が思いつかない"
print(f"{f"{f"{txt}"}"}")
# いい例が思いつかない
リスト文字列の良い感じの整形
# 中央寄せ
for animal in animals:
print(f"{animal: ^12}")
# dog
# goat
# wombat
# cat
# indexやkeyを右寄せする
for idx, animal in enumerate(animals):
print(f"{idx+8: >2}. {animal.capitalize()}")
# 8. Dog
# 9. Goat
# 10. Wombat
# 11. Cat
strftime()
はもう要らない
# 'datetime'を表示するとき、わざわざ'strftime()'する必要はない
now = datetime.now()
print(f"now: {now}")
# now: 2024-06-07 02:29:39.654739
print(f"now: {now:%Y年%m月%d日 %H時%M分}")
# now: 2024年06月07日 02時29分
=指定子の左右スペース
こう動くが、フォーマッタにスペースを消される
res = 600*30*12
print(f"{res=:,}")
# res=216,000
print(f"{res = :,}")
# res = 216,000
print(f"{res= :,}")
# res= 216,000
参考
ここにすべてが書いてある