Python Monthly Topics

t-string⁠テンプレート文字列リテラルの紹介

鈴木たかのり@takanory)です。今月の「Python Monthly Topics」では、Python 3.14の新機能の1つである「テンプレート文字列リテラル(t-string⁠⁠」について紹介します。

t-stringとは

t-stringは、Python 3.14で追加された新しい文字列関連の機能です。t-stringはf-string(フォーマット済み文字列リテラル)と文法は似ていますが、処理結果として文字列ではなく、固定部分と文字列に挿入される部分を表すオブジェクトを返します。

t-stringではf-stringの代わりに文字列のプレフィックスとしてtを指定します。t-stringの実行結果は文字列ではなく、新しいTemplateというデータ型になります。

t-stringの実行結果はTemplate型
>>> name = "takanory"
>>> tstr = t"こんにちは{name}!"  # t-stringを定義
>>> type(tstr)
<class 'string.templatelib.Template'>  # Templateオブジェクトを確認
>>> tstr  # 結果を見やすくするために改行を入れている
Template(strings=('こんにちは', '!'),
         interpolations=(Interpolation('takanory', 'name', None, ''),))
>>> fstr = f"こんにちは{name}!"  # f-stringを定義
>>> type(fstr)  # 文字列型
<class 'str'>
>>> fstr
'こんにちはtakanory!'

t-stringが追加された目的

f-stringと似ていますが異なるオブジェクトTemplate型を返すt-stringは、なぜ追加されたのでしょうか? その背景は、t-stringが言語仕様として提案されたPEP 750に書いてあります。

f-stringは変数などを含んだ文字列を作成するときに非常に便利に使われています。しかしf-stringでは{}の中の補完される値を、文字列の作成時に取得する方法がありません。そのため、不用意にf-stringを使用するとセキュリティ上の問題が発生する可能性があります。

たとえばSQLを作成するときやHTMLを作成するときにf-stringを安易に使用すると、SQLインジェクション[1]やクロスサイトスクリプティング(XSS)[2]などの問題が発生する可能性があります。

f-stringでSQLインジェクションやXSSが発生する例
name = "takanory'; (悪意のあるSQL); --"
sql = f"SELECT * FROM users WHERE name = '{name}'  # 悪意のあるSQLが実行される

name = "<script>(悪意のある処理)</script>"
html = f"<div>Name: {name}</div>"  # 悪意のある処理が実行される

t-stringによって作成されるTemplate型では、固定の文字列と{}で補完される値にそれぞれアクセスする方法を提供することによって、このような問題に対処できます。

Template型の詳解

では、Template型がどういうデータ型かを見てみましょう。Template型は3つの属性を持っています。stringsinterpolationsvaluesです。それぞれ以下の情報が格納されています。

  • strings: 固定の文字列部分のタプル。型は文字列のタプル
  • ingterpolations: {}で記述された箇所を表すInterpolationsというオブジェクトのタプル。interpolationは「加筆、改ざん部分、補間」といった意味があります
  • values: interpolationsの挿入される値のタプル。型はobjectのタプル

Template型の3つの属性を確認します(以降のサンプルコードはすべて見やすさのために、適宜改行を入れています⁠⁠。

Template型の属性を確認
>>> name = "takanory"
>>> tstr = t"こんにちは{name}!"  # t-stringを定義
>>> type(tstr)
<class 'string.templatelib.Template'>  # Template型
>>> tstr
Template(strings=('こんにちは', '!'),
         interpolations=(Interpolation('takanory', 'name', None, ''),))
>>> tstr.strings
('こんにちは', '!')
>>> tstr.interpolations
(Interpolation('takanory', 'name', None, ''),)
>>> tstr.values
('takanory',)

これらの属性をもう少し詳しく見てみます。

{}の前後に固定の文字列が指定されていなくても、stringsには長さ0の文字列が挿入されます。つまり、stringsの数は必ずinterpolationsより1つ多くなります。また、interpolationsvaluesの数は常に同じになります。

Template型の属性を詳しく見る
>>> name = "takanory"
>>> tstr2 = t"{name}"
>>> tstr2.strings  # 固定の文字列がなくても空にはならない
('', '')
>>> tstr3 = t"{name}{name.upper()}"
>>> tstr3.strings  # Interpolationの間にも必ず文字列が入る
('', '', '')
>>> tstr3.interpolations
(Interpolation('takanory', 'name', None, ''),
 Interpolation('TAKANORY', 'name.upper()', None, ''))
>>> tstr4 = t"名前: {name}\n大文字の名前{name.upper()}"
>>> tstr4.strings
('名前: ', '\n大文字の名前', '')
>>> tstr4.interpolations
(Interpolation('takanory', 'name', None, ''),
 Interpolation('TAKANORY', 'name.upper()', None, ''))
>>> tstr4.values
('takanory', 'TAKANORY')

続いてInterpolation型を見ていきます。

Interpolation型の詳解

Interpolation型は以下の4つの属性を持っています。

  • value: 補完される値。型はobject
  • expression: {}の中に書かれた式。!:=が存在する場合はその直前までが格納される。型は文字列
  • conversion: 値valueに対して適用される変換。!に続けて記述する。arsまたはNone
  • format_spec: 値valueに対して適用されるフォーマット指定。:に続けて記述する。型は文字列

なお、Template型のvalues属性はtuple(i.value for i in tstr.interpolations)と同じ意味になります。

Interpolation型の各属性を確認する前に、conversionformat_specがどういうものかをf-stringで確認します。詳細については以下の公式ドキュメントを参照してください。

conversionformat_specの動作をf-stringで確認する
>>> name = "たかのり"
>>> f"{name}"  # conversion、format_specの指定なし
'たかのり'
>>> f"{name!r}"  # !rを指定するとrepr()で変換される
"'たかのり'"
>>> f"{name!a}"  # !aを指定するとascii()で変換される
"'\\u305f\\u304b\\u306e\\u308a'"
>>> num = 12.3456
>>> f"{num:2.2f}"  # 表示桁数を指定
'12.35'
>>> count = 65536
>>> f"{count:#0x}"  # 16進数で表示
'0x10000'
>>> today = date.today()
>>> f"{today:%Y年%m月%d日}"  # 日付のフォーマットを指定
'2025年11月10日'

conversionformat_specについて確認したところで、Interpolationの値について確認します。それぞれ!:の後ろに記述した内容が格納されていることが確認できます。

Interpolation型の各属性を確認する
>>> name = "たかのり"
>>> inter = t"{name!r}".interpolations[0]  # 最初のInterpolationを取得
>>> inter
Interpolation('たかのり', 'name', 'r', '')
>>> inter.value, inter.expression, inter.conversion, inter.format_spec  # 属性を確認
('たかのり', 'name', 'r', '')
>>> num = 12.3456
>>> inter2 = t"{num * 2:2.2f}".interpolations[0]
>>> inter2.value, inter2.expression, inter2.conversion, inter2.format_spec
(24.6912, 'num * 2', None, '2.2f')

なお、t-stringではフォーマット処理は行わないため、format_specにデータ型に対応しないフォーマットを指定してもエラーになりません(f-stringではエラーになります⁠⁠。conversionars以外を指定するとエラーになります。

conversionformat_specに適当な値を指定する
>>> name = "たかのり"
>>> t"{name!f}"  # conversionに正しくない文字を指定するとエラー
  File "<python-input-65>", line 1
    tstr = t"{name!f}"
                   ^
SyntaxError: t-string: invalid conversion character 'f': expected 's', 'r', or 'a'
>>> f"{name:2.2f}"  # f-stringでは対応しないformat_specを指定するとエラー
Traceback (most recent call last):
  File "<python-input-66>", line 1, in <module>
    f"{name:2.2f}"
      ^^^^^^^^^^^
ValueError: Unknown format code 'f' for object of type 'str'
>>> t"{name:2.2f}"  # t-stringではエラーにならない
Template(strings=('', ''),
                interpolations=(Interpolation('たかのり', 'name', None, '2.2f'),))
>>> t"{name:ほげほげ}"  # 適当なformat_specも指定可能
Template(strings=('', ''),
                interpolations=(Interpolation('たかのり', 'name', None, 'ほげほげ'),))

t-stringでセキュリティ問題に対応する

「t-stringが追加された目的」で書いたとおり、t-stringが追加された目的の1つにSQLインジェクションやXSSへの対処があります。ただし、現時点ではt-stringはリリースされたばかりの機能ため、対応しているライブラリなどは限定的です。Python 3.14の標準ライブラリにはとくにt-stringに対応したものはありません。

ここでは、例としてt-stringで記述されたSQLのテンプレートを処理するライブラリ、t-sqlを使います。t-sqlを使用すると、t-stringで定義したSQL文からSQLインジェクションに対処したSQL文の文字列を返します。

t-sqlで安全なSQL文を生成する
>>> import tsql
>>> name = "takanory'; (悪意のある処理); --"
>>> tstr_sql = t"SELECT * FROM users WHERE name = '{name}'"  # t-stringでSQLを作成
>>> sql, values = tsql.render(tstr_sql)  # tsqlでSQLを処理
>>> sql
"SELECT * FROM users WHERE name = '?'"  # ?プレースホルダーを使用
>>> values
["takanory'; (悪意のあるSQL); --"]
>>> sql, _ = tsql.render(tstr_sql, style=tsql.styles.ESCAPED)  # '文字をエスケープ
>>> sql
"SELECT * FROM users WHERE name = ''takanory''; (悪意のあるSQL); --''"

もう1つの例として、tdomでHTMLを処理します。tdomを使用すると、t-stringで定義したHTMLから、XSSに対応したHTMLの文字列を取得できます。

tdomで安全なHTMLを生成する
>>> import tdom
>>> name = "<script>(悪意のある処理)</script>"
>>> tstr_html = t"<div>Name: {name}</div>"  # t-stringでHTMLを作成
>>> tdom_html = tdom.html(tstr_html)  # tdomでHTMLを処理
>>> type(tdom_html)  # 結果はElement型
<class 'tdom.nodes.Element'>
>>> print(tdom_html)  # printするとエスケープされる
<div>Name: &lt;script&gt;(悪意のある処理)&lt;/script&gt;</div>

続いて、簡単なサンプルプログラムで実際にt-stringを処理してみます。

t-stringを処理するプログラムを作成する

ここではt-stringを処理して結果を返すプログラムを作成してみます。まずは単純に値を取り出してみます。実はTemplateオブジェクトはイテレーターに対応しており、固定の文字列とInterpolationを順番に取得できます。ただし、空の文字列は無視されます。

Template文字列から値を取り出す
>>> name = "takanory"
>>> tstr = t"こんにちは{name}!"
>>> tstr  # Templateオブジェクトを確認
Template(strings=('こんにちは', '!'),
         interpolations=(Interpolation('takanory', 'name', None, ''),))
>>> list(tstr)  # リストにすると固定の文字列とInterpolationのリストになる
['こんにちは', Interpolation('takanory', 'name', None, ''), '!']
>>> for item in tstr:  # forループで順番に値を取得
...     if isinstance(item, str):
...         print(f"str: {item}")  # 文字列
...     else:
...         print(f"Interpolation: {item}")  # Interpolation
...
str: こんにちは
Interpolation: Interpolation('takanory', 'name', None, '')
str: !
>>> tstr2 = t"こんにちは{name}{name}"
>>> tstr2
Template(strings=('こんにちは', '', ''),
         interpolations=(Interpolation('takanory', 'name', None, ''),
                         Interpolation('takanory', 'name', None, '')))
>>> list(tstr2)  # リストにすると空文字列は出力されない
['こんにちは',
 Interpolation('takanory', 'name', None, ''),
 Interpolation('takanory', 'name', None, '')]

以下は、安全なHTMLを生成するプログラムの例です。Interpolationの値valuehtmlモジュールのescape()関数を使用してエスケープします。

安全なHTMLを作成する
>>> from html import escape
>>> name = "<script>(悪意のある処理)</script>"
>>> tstr = t"<div>Name: {name}</div>"  # t-stringでHTMLを作成
>>> parts = []  # 結果を格納する変数
>>> for item in tstr:
...     if isinstance(item, str):
...         parts.append(item)
...     else:
...         parts.append(escape(item.value))  # エスケープする
... 
>>> "".join(parts)  # 文字列にまとめる
'<div>Name: &lt;script&gt;(悪意のある処理)&lt;/script&gt;</div>'

Template型のその他の特徴

最後にTemplate型のその他の特徴を紹介します。

Template型はstr()で文字列に変換してもf-stringの結果のような文字列にはなりません。結果はTemplateオブジェクトの情報をそのまま含んだ文字列となり、repr()と同じ結果になります。

Templateを文字列に変換
>>> name = "takanory"
>>> tstr = t"こんにちは{name}!"
>>> str(tstr)  # Templateを文字列に変換
"Template(strings=('こんにちは', '!'), interpolations=(Interpolation('takanory', 'name', None, ''),))"
>>> repr(tstr)  # str()とrepr()の結果は同じ
"Template(strings=('こんにちは', '!'), interpolations=(Interpolation('takanory', 'name', None, ''),))"

また、TemplateオブジェクトはTemplateオブジェクトとのみ連結が可能で、文字列との連結はできません。

Templateの連結
>>> name = "takanory"
>>> tstr = t"こんにちは{name}!"
>>> drink = "beer"
>>> tstr2 = t"おいしい{drink}を楽しもう!"
>>> tstr + tstr2  # Templateを連結
Template(strings=('こんにちは', '!おいしい', 'を楽しもう!')
         interpolations=(Interpolation('takanory', 'name', None, ''),
                         Interpolation('beer', 'drink', None, '')))
>>> tstr + drink  # Templateと文字列の連結はエラーとなる
Traceback (most recent call last):
  File "<python-input-30>", line 1, in <module>
    tstr + drink
    ~~~~~^~~~~~~
TypeError: can only concatenate string.templatelib.Template (not "str") to string.templatelib.Template

t-stringではf-stringと同様に=が使用できます。t-stringの場合は{}の中に記述した式と同じ文字列が、stringsの中に挿入されます。

t-stringとf-stringで=の動作を確認
>>> name = "takanory"
>>> f"こんにちは{name=}!"  # 文字列にname=が挿入される
"こんにちはname='takanory'!"
>>> t"こんにちは{name=}!"  # stringsにname=が挿入される
Template(strings=('こんにちはname=', '!'),
         interpolations=(Interpolation('takanory', 'name', 'r', ''),))
>>> t"{1111*1111=}"  # 式も記述できる
Template(strings=('1111*1111=', ''),
         interpolations=(Interpolation(1234321, '1111*1111', 'r', ''),))

まとめ

Python 3.14で追加されるt-stringとTemplate型について紹介しました。どのような目的で追加されたか、また、データ構造やデータの扱い方について理解できましたでしょうか。

現時点ではt-stringはリリースされたばかりで、大きな新機能ではありますが、対応するライブラリも少ないため「活用はこれから」という印象です。今後t-stringに対応、活用したさまざまなライブラリが増えてくるのではないかと期待しています。

t-stringを考案した人たちも想像していないような、便利な面白い使い方が出てくるかもしれません。t-stringの周辺の状況にこれから注目していきたいと思います。

参考資料

おすすめ記事

記事・ニュース一覧