f-string との違いを整理する
Python 3.14 では、新しく t-string が追加されました。正式には template string literal と呼ばれる機能です。見た目は f"..." によく似ていますが、役割はかなり違います。f-string が最終的な str を作るのに対して、t-string は 文字列の固定部分と埋め込み部分を分けたまま持てる Template オブジェクト を返します。
まず f-string との違い から整理します。結論だけ先に言うと、こうです。
f-string は完成した文字列を作るもの
t-string は文字列の部品を持つもの
この違いがわかると、t-string の役割はかなり理解しやすくなります。
まずは f-string をおさらい
f-string は、変数や式の結果を文字列の中に埋め込んで、その場で完成した文字列を作る仕組みです。
name = "Taro"
age = 20
message = f"{name} is {age} years old."
print(message)
実行結果:
Taro is 20 years old.
ここで message に入るのは、普通の文字列である str です。f-string では {...} の中の式を評価したあと、必要なら !r などの変換や :.2f のような書式指定を適用し、最終的な文字列に仕上げます。
t-string は何が違うのか
t-string も見た目はよく似ています。
name = "Taro"
message = t"Hello, {name}"
ただし、ここで返ってくるのは "Hello, Taro" という完成済みの文字列ではありません。返るのは string.templatelib.Template というオブジェクトです。t-string は、文字列をすぐ 1 本にまとめるのではなく、固定の文字列部分 と 埋め込み部分 を分けて持てるようになっています。
つまり、同じように書けても意味はこう違います。
-
f"Hello, {name}"
→"Hello, Taro"という完成品を作る -
t"Hello, {name}"
→"Hello, "と{name}の情報を持つテンプレートを作る
この違いが、f-string と t-string の本質です。 ([Python documentation][2])
f-string と t-string の違いを表で見る
| 項目 | f-string | t-string |
|---|---|---|
| 書き方 | f"Hello {name}" |
t"Hello {name}" |
| 返り値 | str |
string.templatelib.Template |
| 何ができるか | すぐに完成した文字列を作る | 固定部分と埋め込み部分を分けて持つ |
{...} の式 |
その場で評価される | その場で評価される |
!r, !s, !a
|
自動で適用される | 自動適用されず、情報として保持される |
:.2f などの書式指定 |
format() が適用される |
書式情報として保持され、処理側がどう使うか決める |
| 主な用途 | 画面表示、ログ、メッセージ生成 | カスタム文字列処理、HTML/SQL/DSL/構造化ログなど |
| 普段使いしやすさ | 高い | 用途がはっきりしている場面向け |
この表で特に大事なのは、どちらも式自体はその場で評価されることです。t-string は「遅延評価」ではありません。違うのは、評価後にそれをすぐ完成文字列へ変換するか、それともテンプレート構造として保持するかです。 ([Python documentation][3])
実際に型を見ると違いがはっきりする
まずは f-string です。
name = "Taro"
x = f"Hello, {name}"
print(x)
print(type(x))
出力:
Hello, Taro
<class 'str'>
次に t-string です。
name = "Taro"
x = t"Hello, {name}"
print(type(x))
print(x.strings)
print(x.values)
print(x.interpolations)
出力:
<class 'string.templatelib.Template'>
('Hello, ', '')
('Taro',)
(Interpolation('Taro', 'name', None, ''),)
t-string では、strings に固定文字列、values に評価済みの値、interpolations に補間情報が入ります。Template はこのように、文字列を分解したまま扱えるのが特徴です。
t-string の中には何が入っているのか
たとえば次のコードを見てみます。
name = "Taro"
tpl = t"Hello, {name}!"
このとき、t-string の中にはざっくり次のような情報があります。
- 固定文字列:
"Hello, "と"!" - 埋め込み部分:
{name} - 評価された値:
"Taro"
公式ドキュメントでも、Template は 静的な文字列部分 と 補間部分 を保持し、strings、interpolations、values として参照できると説明されています。 ([Python documentation][4])
また、list(template) のように反復すると、固定文字列と Interpolation オブジェクトを順番に取り出せます。
name = "Taro"
tpl = t"Hello, {name}!"
print(list(tpl))
出力:
['Hello, ', Interpolation('Taro', 'name', None, ''), '!']
こうして見ると、t-string は「文字列を作る機能」というより、文字列の設計図を持つ機能 と考えたほうがわかりやすいです。
では、t-string は何に使うのか
ここが一番気になるポイントだと思います。普通に文字列を作りたいだけなら、たいていは f-string で十分です。t-string が向いているのは、埋め込み値をそのまま文字列にしてしまう前に、チェックや変換を挟みたい場面 です。
PEP 750 では、たとえば次のような用途が想定されています。
- HTML を安全に組み立てる
- SQL のような文字列を安全に扱う
- 独自のテンプレートエンジンを作る
- 構造化ログを扱う
f-string では埋め込み値がそのまま最終文字列に入ってしまうため、途中で安全な変換や独自ルールを挟みにくい、という背景があります。t-string はそこを補うために追加されました。
t-string で HTML を安全に組み立てる
t-string が役立つ場面のひとつが、HTML を組み立てるときです。Python 3.14 の t-string は、f-string と似た書き方で値を埋め込めますが、完成済みの str ではなく string.templatelib.Template を返します。そのため、固定の HTML 部分 と 埋め込み値 を分けて扱えます。Python 公式ドキュメントでも、t-string は「custom string processing」のための仕組みとして説明されています。
まずは危ない例
たとえば、ユーザー入力をそのまま f-string で HTML に差し込むコードを考えます。
user_name = '<script>alert("XSS")</script>'
comment = 'Hello <b>world</b>'
html_text = f"""
<div class="card">
<h2>{user_name}</h2>
<p>{comment}</p>
</div>
"""
print(html_text)
出力:
<div class="card">
<h2><script>alert("XSS")</script></h2>
<p>Hello <b>world</b></p>
</div>
この書き方だと、user_name や comment に入っている <script> や <b> のような文字列が、そのまま HTML の一部として混ざります。f-string は最終的な文字列をその場で完成させるので、「埋め込み値だけ安全に変換する」処理をあとから挟みにくいのが弱点です。t-string はこの問題に対して、補間値を途中で受け取って変換できるようにするための仕組みです。
t-string を使った安全な組み立て
次は、t-string で受け取った補間値だけを HTML エスケープしてから連結する例です。
from html import escape
from string.templatelib import Interpolation, Template
def render_html(template: Template) -> str:
"""t-string を受け取り、補間値だけ HTML エスケープして連結する。"""
parts: list[str] = []
for part in template:
if isinstance(part, Interpolation):
# 動的に入る値だけエスケープ
parts.append(escape(str(part.value), quote=True))
else:
# 固定文字列はそのまま使う
parts.append(part)
return "".join(parts)
user_name = '<script>alert("XSS")</script>'
comment = 'Hello <b>world</b>'
profile_url = 'https://example.com/?q="python"&page=1'
page = render_html(t"""
<div class="card">
<h2>{user_name}</h2>
<p>{comment}</p>
<a href="{profile_url}">profile</a>
</div>
""")
print(page)
出力:
<div class="card">
<h2><script>alert("XSS")</script></h2>
<p>Hello <b>world</b></p>
<a href="https://example.com/?q="python"&page=1">profile</a>
</div>
このコードのポイントは、HTML 全体をエスケープしているのではなく、{...} で埋め込まれた値だけをエスケープしていることです。html.escape() は &、<、> を HTML 安全な文字列に変換し、quote=True のときは " と ' も変換します。Python 公式ドキュメントでも、これは引用符で囲まれた HTML 属性値に文字列を入れるときに役立つと説明されています。
このコードが t-string らしい理由
このサンプルが t-string の良さをよく表しているのは、固定文字列と埋め込み値を別々に扱えている点です。Template は固定部分と補間部分の情報を保持していて、反復すると文字列部分と Interpolation オブジェクトを順番に取り出せます。だから、今回のように「固定の HTML タグはそのまま使う」「埋め込み値だけを escape() に通す」といった処理が素直に書けます。 ([Python documentation][1])
f-string では、f"...{value}..." と書いた時点で、固定文字列と埋め込み値がひとつの str にまとまってしまいます。一方 t-string では、文字列になる前の段階で補間値を横取りできるので、安全な変換や独自ルールを差し込みやすくなります。これは PEP 750 で説明されている、t-string の代表的な狙いのひとつです。
この例を一言でまとめると、次のようになります。
- f-string は、値をそのまま混ぜて完成した HTML 文字列を作る
- t-string は、値を混ぜる前に受け取れるので、安全な変換をしてから HTML を作れる
t-string は、普段の表示メッセージを作るだけなら少し大げさです。ただ、HTML や SQL のように、埋め込み値をそのまま入れると困る文字列を扱うときには、かなりわかりやすい使い道があります。PEP 750 でも、こうした用途が主要なユースケースとして挙げられています。
補足
このサンプルは、「ユーザー入力を HTML に埋め込む前に最低限エスケープする」 ための最小例です。実際の Web アプリケーションでは、テンプレートエンジン側の自動エスケープ機能を使うことも多いですが、t-string の考え方を理解するにはとても良い題材です。Python 3.14 の t-string は、まさにこうした「補間値を処理してから最終文字列を作る」用途のために追加されています。
注意点
t-string でも式はその場で評価される
t-string を見ると、「あとで処理するなら、式の評価もあとになるのかな」と思うかもしれません。ですが、そこは違います。Python 3.14 の t-string は、構文と評価ルール自体は f-string と同じです。つまり {...} の中の式は、その場で評価されます。
たとえば次のコードでは、upper() は t-string を作る時点で実行されます。
name = "Taro"
tpl = t"Hello, {name.upper()}"
PEP 750 でも、補間の遅延評価案は検討されたものの採用されませんでした。つまり t-string は「後で式を評価する仕組み」ではなく、「評価した結果をどのように最終文字列へするかを後で決められる仕組み」です。
!r や :.2f は f-string と同じではない
見た目が似ているので、ここも勘違いしやすいポイントです。
f-string では、たとえば !r や :.2f はその場で適用されて、最終的な文字列に反映されます。
一方 t-string では、それらは 自動で適用されず、Interpolation オブジェクトに 変換指定や書式指定の情報として保持 されます。どう扱うかは、t-string を処理する側のコードが決めます。
必要であれば、string.templatelib.convert() を使って f-string に近い !s、!r、!a の挙動を再現できます。つまり t-string は、既存の文字列整形を置き換えるというより、整形ルールを自分で組み立てるための土台 です。
普段のコードでは、どちらを使えばいいのか
初学者の段階では、次のように覚えるのが一番実用的です。
- 画面表示や通常のログ出力など、普通に文字列を作りたい
→ f-string - 埋め込み値をあとで検査したり、独自ルールで安全に組み立てたい
→ t-string
Python 3.14 で t-string が入ったからといって、日常の文字列処理をすべて t-string に置き換える必要はありません。公式にも、t-string は custom string processing のための仕組みとして位置づけられています。
まとめ
Python 3.14 の t-string は、見た目こそ f-string に似ていますが、役割はかなり違います。
- f-string は、完成した文字列を作る
- t-string は、文字列の固定部分と埋め込み部分を分けたまま持つ
この違いを一言で言うなら、
f-string は完成品
t-string は材料のまま
です。t-string は、普段のメッセージ表示のための機能というより、安全な変換や独自の文字列処理をしやすくするための新しい仕組み と考えると理解しやすいです。