Python 3.14で登場したテンプレート文字列(t文字列)とは? f文字じゃダメなの?Python最新情報キャッチアップ

Pythonのf文字列は便利ですが、ちょっとした問題もあります。それを解決するために、Python 3.14ではテンプレート文字列が導入されました。f文字列の問題、t文字列とは、その基本的な使い方を紹介します。

» 2025年10月20日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「Python最新情報キャッチアップ」のインデックス

連載目次

 Python 3.14では新たにテンプレート文字列(以下、t文字列)と呼ばれる新たな文字列リテラルが導入された。手短にいえばこれは「f文字列内に何らかの値を埋め込む前に処理を介入させることで、より安全な文字列補間を行える」ようにしたものだ。t文字列はPEP 750で提案されている(ただし、これを説明する公式なドキュメントは「string.templatelib --- Support for template string literals」となる)。

 以下ではt文字列について簡単にまとめていく。

f文字列の問題

 以下のコードを見てほしい。

import os

user_input = 'hp_kawasaki'  # 実際にはユーザー入力を受け取るものとする
cmd_to_exec = f'echo hello {user_input}'
os.system(cmd_to_exec)

ユーザーから名前を受け取り、それをos.system関数で実行する

 これはuser_inputに入力された名前を受け取り、それを基に「echo <名前>」という文字列を組み立てて、それをos.system関数で実行するコードだ。


かわさき

 どうもHPかわさきです。

 ここでは「なぜos.system関数を使うの?」という質問はなしということで。あからさまに危険なコードの例だと思ってください。また、user_inputには'hp_kawasaki'を決め打ちで代入していますが、これも何らかの手段でユーザー入力を受け取るものだと考えてください。


 このコードを例えば、macOSで実行すると次のようになる。

コンソールに「hello hp_kawasaki」と表示された コンソールに「hello hp_kawasaki」と表示された

 コンソールに「hello hp_kawasaki」と表示された。その下の「0」は問題なく実行が終わったことを意味する終了コードだ。このようにf文字列は手軽に、文字列に何らかの値を埋め込む(補間する)ことができる便利な仕組みである。

 しかし、user_inputが次のようなものだったらどうだろう。

import os

user_input = 'hp_kawasaki; touch newfile.txt; ls -l newfile.txt'
cmd_to_exec = f'echo hello {user_input}'
os.system(cmd_to_exec)

user_inputに余計なテキストが追加されている

 「hp_kawasaki」に続けて「; touch newfile.txt; ls -l newfile.txt」とある。これを実行すると次のようになる。

touchコマンドで新規にファイルが作成され、lsコマンドでその情報が表示されたところ touchコマンドで新規にファイルが作成され、lsコマンドでその情報が表示されたところ

 「hp_kawasaki」の後ろにあるセミコロン「;」はf文字列にあるechoコマンドを終了させる。この結果、メッセージを表示した後、touchコマンドでnewfile.txtというファイルが新規に作成され、lsコマンドでその情報が表示されている。

 ここではファイルの作成とその情報の表示だけだったが、f文字列ではこのようなプログラマーが想定していない値を受け取った場合に、それをf文字列では置換フィールド(波かっこ「{}」に囲まれた部分)に記述された式は、f文字列が評価されるときに即座に評価され、その値がf文字列に埋め込まれる。そのため、危険なコードが含まれていても、それを事前に無害化する処理などを差し込めないのが弱点といえる。


かわさき

 ここでは波かっこ「{}」で囲まれた部分を「置換フィールド」と呼んでいますが、ドキュメントにはこれが置換フィールドという名前だとは明言されてないんですよね。でも、分かりやすいってことで、ここでは置換フィールドと呼ぶことにします。


t文字列とは

 今述べたf文字列の問題点に対処するために生まれたのがt文字列といえる。t文字列は通常の文字列(f文字列を含む)のような見た目に分かりやすいイミュータブル値ではない。実際にt文字列を作成してみよう。

user_input = 'hp_kawasaki'
cmd_to_exec = t'echo hello {user_input}'
print(cmd_to_exec)

t文字列の作成

 これを実行すると、その結果は次のようになる。

t文字列は多くの要素で構成されるイミュータブル値 t文字列は多くの要素で構成されるイミュータブル値

 'hello hp_kawasaki'のようなシンプルな文字列リテラルを予想していると、ちょっとビックリするかもしれない。t文字列はstring.templatelibモジュールで定義されているTemplateクラスのインスタンスであり、ちょっと複雑な構造を持ったイミュータブルなオブジェクトである。

 Templateオブジェクトには重要な属性が3つある。

  • strings属性:置換フィールドを区切りとしてt文字列を分割した際の置換フィールド以外の部分を格納するタプル。t文字列の静的な部分を格納する
  • interpolations属性:string.templatelib.Interpolationクラスのオブジェクトを要素とするタプル。Interpolationオブジェクトは各置換フィールドの値、そこに記述された式、文字列化する際の手段、書式化文字列などを属性として持つ。t文字列の補完部分を格納する
  • values属性:置換フィールドに書かれた式の値だけを格納するタプル

 上に示した実行結果ではstrings属性とinterpolations属性のみが表示されている点には注意しよう。上のコードで作成したt文字列が実際にはどのような形で上記の属性に格納されているのかを以下に示す。

記述されたt文字列とTemplateオブジェクトの属性の対応関係 記述されたt文字列とTemplateオブジェクトの属性の対応関係

 記述した置換フィールドは1つだけなので、ここではinterpolations属性は要素を1つだけ持つタプルとなっていることには注意されたい。置換フィールドを複数記述すれば、interpolations属性の要素数も複数になる。Interpolationクラスのオブジェクトが持つ実際の値、式、文字列化の方法、書式化文字列はvalue、expression、conversion、format_specの各属性を使っても取得できる。

 ここで重要なのはstrings属性に格納されるのは、プログラマーがt文字列内に自ら記述する文字列リテラルなので自分での制御が可能、つまり安全だと見なしてもよさそうなことだ。対して、interpolations属性には外部から入力された危険な値が格納される可能性がある。よって、無害化などの処理は主にこちらを対象に行うことになるだろう。

 values属性はinterpolations属性に格納されるInterpolationオブジェクトの値(value属性)を格納している。置換フィールドに書かれた式の値を取得するだけなら、この属性が使えるかもしれない(最後に見るように何らかの前処理を行うときには、values属性に格納されている値は最終的に得られる文字列に埋め込まれる値とは異なる可能性がある点に注意)。

foo = 'FOO'
bar = 'BAR'
tstr = t'foo: {foo}, bar: {bar}'

for val in tstr.values:
    print(val)  # 'FOO'と'BAR'が出力される

values属性を使うと補間された値(この場合は'FOO'と'BAR')を反復できる

 最後にt文字列(Templateオブジェクト)は反復可能オブジェクトでもある。例えば、for文でこれを使うと、strings属性とinterpolations属性に含まれるそれぞれの値がt文字列で登場する順番に反復される。この機構を使って、t文字列に埋め込まれる値を処理しながら、最終的な結果(文字列)を構築するというのがt文字列のよくある使い方となるだろう。

t文字列の基本的な使い方

 今述べたt文字列の各要素を反復処理していくサンプルとして、先ほど紹介したf文字列には問題があったが、t文字列を使ってこれを無害化してみよう。以下にコードを再掲する。

import os

user_input = 'hp_kawasaki; touch newfile.txt; ls -l newfile.txt'
cmd_to_exec = f'echo hello {user_input}'
os.system(cmd_to_exec)

ファイルを新規作成して、その情報を表示してしまうf文字列

 このf文字列にはセミコロンの後にファイルを新規に作成し、その情報を表示するシェルコマンドが含まれている。単純にこれを無害化するにはuser_input全体をクオートで囲って「echo 'hp_kawasaki; touch newfile.txt; ls -l newfile.txt'」がos.system関数で実行されるようにしてしまえばよい。これにはshlexモジュールのquote関数を使うのが簡単だ。


かわさき

 shlexモジュールはUNIX系統のOS向けに設計されているので、Windowsで使う際には正しくエスケープできるかの保証がないことには注意してください。


 まず、cmd_to_execにf文字列ではなくt文字列を代入しよう。

import os

user_input = 'hp_kawasaki; touch newfile.txt; ls -l newfile.txt'
cmd_to_exec = t'echo hello {user_input}'

f文字列ではなくt文字列をcmd_to_execに代入

 これをos.system関数に渡しても、これが文字列でないために例外が発生する。そこで、これを無害化して文字列に変換する何らかの処理が必要となる。ここでは以下のような関数を定義する。

from string.templatelib import Interpolation
from shlex import quote

def escape(tstr):
    result = []
    for part in tstr:
        if isinstance(part, Interpolation):
            tmp = quote(part.value)
            result.append(tmp)
        else:
            result.append(part)
    return ''.join(result)

補間された値をクオートで囲んだ結果を、t文字列内のstrings属性の値と結合する

 escape関数では次のような処理を行っている。

  • for文でt文字列を構成する各要素を反復する(ループ変数partに代入される)
  • 反復時には「'echo hello '」(文字列)→「'hp_kawasaki; touch newfile.txt; ls -l newfile.txt'」(を表すInterpolationオブジェクト)→「''」(t文字列の最後の静的部分が空文字列のため)の順で反復される
  • 純粋な文字列については結果を格納する変数resultにそのまま追加する
  • Interpolationオブジェクトについては、補間された値(Interpolationオブジェクトのvalue属性。この場合は'hp_kawasaki; touch newfile.txt; ls -l newfile.txt')をshlex.quote関数でエスケープして、それをresultに追加する
  • 最後にresultの内容を結合して戻り値とする

 for文で各要素を反復し、その中のif文で要素がInterpolationオブジェクトかどうかに応じて処理を切り分け、最後にそれらを結合して文字列を得るというのが王道的なやり方といえる。

 後はこの関数を呼び出してt文字列から通常の文字列を作成し、それをos.system関数に渡すだけだ。全てのコードをまとめると次のようになる。

import os
from string.templatelib import Interpolation
from shlex import quote

def escape(tstr):
    result = []
    for part in tstr:
        if isinstance(part, Interpolation):
            tmp = quote(part.value)
            result.append(tmp)
        else:
            result.append(part)
    return ''.join(result)

user_input = 'hp_kawasaki; touch newfile.txt; ls -l newfile.txt'
cmd_to_exec = t'echo hello {user_input}'
result = escape(cmd_to_exec)
os.system(result)

t文字列を処理して文字列を得て、それをos.system関数に渡して実行

 実行結果を以下に示す。

セミコロン以降を含む全てがechoコマンドで表示されるようになった セミコロン以降を含む全てがechoコマンドで表示されるようになった

 エスケープによって元のuser_inputにあったセミコロン以降のtouchコマンドとlsコマンドも含めてechoコマンドに渡されるようになり、勝手にファイルが作成されたり、lsコマンドが実行されたりしなくなったのが分かる。

 ここではt文字列が生まれた大きな理由であると思われる「文字列への値の安全な埋め込み」の例としたが、値を文字列に埋め込む前に何らかの処理を差し挟みたいというときにはt文字列を使うと何かよいことがあるかもしれない。


かわさき

 「何かよいことが」と書きましたが、ちょっとまだ具体的にこんなことができて便利だよ、というのは思い付いていないんですよね。思い付いたらPythonクイズの問題にでもしたいと思います(笑)。


「Python最新情報キャッチアップ」のインデックス

Python最新情報キャッチアップ

Copyright© Digital Advantage Corp. All Rights Reserved.

アイティメディアからのお知らせ

スポンサーからのお知らせPR

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。