Pythonのf-stringは、可読性と表現力を両立しながら文字列に変数や式を直接埋め込める構文です。
従来のstr.format
や%
演算子よりも簡潔に書け、実行速度面でも有利な場面が多いのが特長です。
本記事では、基本構文から書式指定、デバッグや実務での活用、他方式との違い、注意点まで体系的に解説します。
Pythonのf-stringとは?文字列に変数を埋め込む基本
f-stringの概要とメリット(可読性・パフォーマンス・簡潔さ)
f-stringは、文字列リテラルの先頭にf
またはF
を付け、波括弧{}
の中に変数や式を書いて評価結果を差し込む構文です。
テンプレートと値の対応が1カ所にまとまるため読みやすく、冗長なインデックス指定や位置・名前の不整合が起きにくいという利点があります。
CPythonでは多くのケースでstr.format
や%
演算子より高速に動作する傾向があり、短いメッセージ生成の繰り返しなどで有効です。
f-stringが使えるPythonバージョンと前提条件(Python 3.6+)
f-stringはPython 3.6で導入されました。
デバッグ用途の=(例:f"{var=}")
はPython 3.8以降で利用可能です。
基本的な利用には追加パッケージは不要ですが、日時や数値のロケール別表現まで踏み込む場合はlocale
やbabel
といった外部ライブラリの併用を検討します。
f-stringの基本構文と使い方
f接頭辞と{}の基本ルール
文字列の先頭にf
を付与し、{}
の中に式を書きます。
式は評価され、そのstr()
相当の文字列が差し込まれます。
# 基本的なf-stringの例
name = "Alice"
age = 30
message = f"{name}は{age}歳です"
print(message)
Aliceは30歳です
文字列接頭辞の組み合わせ
- 生文字列と併用(raw f-string):
rf"..."
(バックスラッシュの解釈に注意) - バイト列では不可:
f
はb
と併用できません(fb"..."
はエラー)
変数・式・関数呼び出しの埋め込み
{}
の中には任意のPython式が書けます。
関数呼び出しや条件演算子、算術演算の結果も直接埋め込めます。
# 式や関数の結果をそのまま埋め込む
def area(w, h):
return w * h
w, h = 7, 5
age = 30
double_age = age * 2
print(f"面積: {area(w, h)} / 年齢の2倍: {double_age} / 条件: {'成人' if age >= 20 else '未成年'}")
面積: 35 / 年齢の2倍: 60 / 条件: 成人
ブレースのエスケープ方法({{、}})
テンプレートとして{
や}
を文字として出したい場合は二重にします。
# ブレースの出力(テンプレート用途など)
name = "Alice"
print(f"プレースホルダーは {{name}} のように書きます。実際の名前は {name} です。")
プレースホルダーは {name} のように書きます。実際の名前は Alice です。
f-stringの書式指定(format spec)と便利テクニック
数値フォーマット:小数点・桁区切り・指数表記(:.2f, :, e)
:
に続けて「フォーマット指定子(format spec)」を書くと、数値の表記を細かく制御できます。
# 数値の書式指定いろいろ
x = 12345.6789
print(f"小数2桁: {x:.2f}") # 固定小数点
print(f"桁区切り: {x:,.2f}") # 3桁区切りカンマ
print(f"指数表記: {x:.3e}") # 指数表記
print(f"符号付き: {x:+.1f}") # 常に符号表示
print(f"ゼロ埋め幅10: {int(x):010d}") # 幅10で0埋め
小数2桁: 12345.68
桁区切り: 12,345.68
指数表記: 1.235e+04
符号付き: +12345.7
ゼロ埋め幅10: 0000012345
主な指定子の概要
- 浮動小数:
f
(固定小数点)、e
/E
(指数)、g
(簡潔な表現) - 整数:
d
(10進)、b
(2進)、o
(8進)、x
/X
(16進)、#
で接頭辞付与 ,
で桁区切り、.
の後に精度、+
/0
でゼロ埋め
文字列の幅揃え・左寄せ/右寄せ/中央寄せ(:<, :>, :^)
幅と配置は以下のように指定します。
# 幅と寄せ、フィル文字
s = "cat"
print(f"|{s:<10}|") # 左寄せ
print(f"|{s:>10}|") # 右寄せ
print(f"|{s:^10}|") # 中央寄せ
print(f"|{s:.<10}|") # '.'で右側を埋める左寄せ
|cat |
| cat|
| cat |
|cat.......|
日付時刻のフォーマット(datetimeと%Y-%m-%d)
datetime
は__format__
でstrftime
と同様の書式に対応します。
{dt:%Y-%m-%d}
のように指定できます。
from datetime import datetime, timezone, timedelta
# 現在日時をUTC+9で
dt = datetime.now(timezone(timedelta(hours=9)))
print(f"ISO: {dt:%Y-%m-%dT%H:%M:%S%z}")
print(f"日付のみ: {dt:%Y-%m-%d}")
print(f"時刻のみ: {dt:%H:%M}")
ISO: 2025-08-19T..:..:..+0900
日付のみ: 2025-08-19
時刻のみ: ..:..
(実際の時刻は実行時に応じて異なります)
デバッグ用=指定子(f”{var=}”)と!r/!s/!aの変換指定
=指定子
は「式=値」をそのまま出力してくれます。
!r
はrepr()
、!s
はstr()
、!a
はascii()
での表示に切り替えます。
# デバッグ出力に便利な表現
text = "こんにちは\n世界"
n = 42
print(f"{n=}")
print(f"{text=}") # 改行やエスケープが見える
print(f"{text!r}") # repr()
print(f"{text!s}") # str()
print(f"{text!a}") # ascii()
n=42
text='こんにちは\n世界'
'こんにちは\n世界'
こんにちは
世界
'\u3053\u3093\u306b\u3061\u306f\n\u4e16\u754c'
str.format/%演算子との違いと使い分け
可読性・保守性・エラー検出の比較
テンプレート内に式を書けるf-stringは、値の由来が直感的で保守性が高いです。
str.format
は位置/名前で対応付けるため、引数のミスが実行時まで発見されにくい場合があります。
%
演算子はC言語ライクで歴史は長いものの、型指定の厳格さやタプル・辞書の渡し方に癖があります。
観点 | f-string | str.format | % 演算子 |
---|---|---|---|
可読性 | 高い(式が直書き) | 中(名前や位置で参照) | 低〜中(書式が古典的) |
柔軟性 | 高い(任意の式) | 高い(ネスト/インデックス) | 中(機能は限定的) |
エラー検出 | 良(評価時に直ちに) | 普通 | 普通 |
推奨度 | 新規コードで推奨 | 既存互換や複雑テンプレで有用 | レガシー互換・一部i18nで有用 |
パフォーマンス比較とマイクロベンチマークの傾向
一般にf-stringが最速、次いで%
、str.format
が最も遅い傾向があります。
ただし差はケースにより変わります。
参考として簡易ベンチマークを示します。
# マイクロベンチマーク(環境により結果は変わります)
import timeit
setup = "name='Alice'; age=30; x=12345.6789"
t_f = timeit.timeit("f'{name} {age} {x:.2f}'", setup=setup, number=1_000_000)
t_format = timeit.timeit("'{} {} {:.2f}'.format(name, age, x)", setup=setup, number=1_000_000)
t_percent = timeit.timeit("'%s %d %.2f' % (name, age, x)", setup=setup, number=1_000_000)
print(f"f-string : {t_f:.3f}s")
print(f"str.format : {t_format:.3f}s")
print(f"% operator : {t_percent:.3f}s")
f-string : 0.250s
str.format : 0.420s
% operator : 0.310s
(上記は一例です。
実機・Pythonバージョンで値は変わります。
過度な最適化より可読性を優先し、ボトルネックがあれば測定のうえ最適化してください)
既存コードからf-stringへ移行する際のポイント
既存のstr.format
や%
を機械的に置換すると、ロケールや翻訳(gettext)のプレースホルダーが崩れることがあります。
単純なログやメッセージから段階的に移行し、以下を意識します。
翻訳文字列には名前付きプレースホルダー(%(name)s
や{name}
)を使い、抽出・翻訳のワークフローを壊さないようにします。
また、SQLやシェルコマンドなど安全性が重要な箇所は、f-stringによる組み立てを避け、パラメータバインディングや引数リスト化を使用します。
実務で使えるf-stringサンプルコード集
ログ出力・SQL・ファイルパス組み立てでの安全な使い方
実務では「どこまでf-stringで良いか」を判断することが重要です。
# ログ出力:推奨はロガーの遅延フォーマット(f-stringは必要時のみ)
import logging
logging.basicConfig(level=logging.INFO)
user = "alice"
items = 3
# 推奨:ロガーに引数を渡す(無効レベル時にコストがかからない)
logging.info("user=%s items=%d", user, items)
# f-stringは簡潔だが、無効レベルでも評価される点に留意
logging.info(f"user={user} items={items}")
INFO:root:user=alice items=3
INFO:root:user=alice items=3
# SQL:f-stringでSQLを組み立てない(インジェクション対策)
import sqlite3
conn = sqlite3.connect(":memory:")
cur = conn.cursor()
cur.execute("CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT)")
cur.executemany("INSERT INTO users(name) VALUES (?)", [("alice",), ("bob",)])
name = "alice' OR '1'='1" # 悪意ある入力の例(インジェクション)
# 悪い例(やってはいけない)
# cur.execute(f"SELECT * FROM users WHERE name = '{name}'")
# 良い例:プレースホルダーでバインド
cur.execute("SELECT * FROM users WHERE name = ?", (name,))
print(cur.fetchall()) # 該当なし
[]
# ファイルパス:pathlibで結合+f-stringでファイル名の一部を表現
from pathlib import Path
from datetime import date
base = Path("output")
base.mkdir(exist_ok=True)
today = date.today()
path = base / f"report_{today:%Y%m%d}.csv"
print(path)
output/report_20250819.csv
通貨・百分率・単位の表現(₹/$、%表示、単位付き数値)
通貨記号は簡単な用途ならプレフィックス/サフィックスで十分です。
厳密なロケール対応は後述します。
amount = 1234567.8
rate = 0.0375
weight = 12.3456
print(f"${amount:,.2f}") # 米ドル風
print(f"₹{amount:,.2f}") # ルピー記号の付与(桁区切りはロケール非対応)
print(f"{rate:.2%}") # 3.75%
print(f"{weight:.1f} kg") # 単位付き
$1,234,567.80
₹1,234,567.80
3.75%
12.3 kg
多言語メッセージとロケール対応の注意点
翻訳文字列はプレースホルダーを含む「完成形」をmsgidとして抽出できる形にします。
gettextでは%(name)s
のような名前付き置換が扱いやすいです。
# gettextの例:翻訳側が並び替え可能なよう名前付きにする
import gettext
_ = lambda s: s # 実演のためダミー
name, city = "Alice", "Tokyo"
msg = _("Hello, %(name)s from %(city)s") % {"name": name, "city": city}
print(msg)
Hello, Alice from Tokyo
ロケールに応じた通貨表記や桁区切りはlocale
やbabel
の利用が現実的です。
f-stringの中でbabel.numbers.format_currency
の結果を埋め込むのが扱いやすい方法です。
# Babelで通貨のロケール表記
from babel.numbers import format_currency
amount = 1234567.8
print(f"{format_currency(amount, 'USD', locale='en_US')}")
print(f"{format_currency(amount, 'INR', locale='hi_IN')}")
print(f"{format_currency(amount, 'EUR', locale='de_DE')}")
$1,234,567.80
₹12,34,567.80
1.234.567,80 €
f-stringの注意点・ベストプラクティス
セキュリティ:ユーザー入力の埋め込みとインジェクション対策
f-stringは式を評価して文字列化するだけで、入力の無害化は行いません。
SQLは必ずパラメータバインディング、シェルは引数リストで渡す、HTMLはテンプレートエンジンやエスケープ関数を使うことが基本です。
たとえばsubprocess.run
ではshell=True
を避け、["cmd", arg]
のように引数を分割して渡します。
ログへの出力も、潜在的に機密情報を含む値にはマスキングを施します。
型とNoneの扱い・__format__の活用
{value}
はstr(value)
、{value:spec}
はformat(value, spec)
が呼ばれます。
None
はstr(None)
で"None"
になりますが、数値フォーマットを要求するとTypeError
になります。
安全に扱うにはデフォルト値や条件を明示します。
# None安全な出力例
value = None
print(f"value={value if value is not None else 'N/A'}")
value=N/A
独自クラスでは__format__(self, spec)
を実装すると、f-stringの書式指定に対応できます。
# カスタムクラスの__format__実装例
class Point:
def __init__(self, x, y): self.x, self.y = x, y
def __format__(self, spec: str) -> str:
# "(), []"などの囲み指定、デフォルトは角括弧
if spec == "paren":
return f"({self.x}, {self.y})"
return f"[{self.x}, {self.y}]"
p = Point(3, 5)
print(f"{p}") # 既定
print(f"{p:paren}") # paren指定
[3, 5]
(3, 5)
長いテンプレートや国際化には他手段(template/翻訳)を検討
複数行にまたがる長いメール本文やHTML、翻訳が絡む文面では、f-stringだけで組み立てると可読性が落ちます。
以下の手段を適材適所で使い分けます。
string.Template
でのプレースホルダー置換(安全性重視の簡易テンプレート)- Jinja2などのテンプレートエンジン(HTMLや大規模テキスト)
- gettextの名前付きプレースホルダー(
%(name)s
)で翻訳者が順序を調整可能に
# string.Templateの例
from string import Template
tpl = Template("Hello $name,\nYour balance is $amount.")
text = tpl.substitute(name="Alice", amount=f"${1234.5:,.2f}")
print(text)
Hello Alice,
Your balance is $1,234.50.
まとめ
f-stringは、Pythonにおける文字列整形の第一選択肢と言える強力な機能です。
f"..."
と{}
だけで変数・式・関数の結果を簡潔に埋め込め、:.2f
や:,
などの書式指定で表記も自在に制御できます。
f"{var=}"
や!r
はデバッグを強力に支援し、日付の%Y-%m-%d
指定や桁区切りなど日常的な整形をシンプルにします。
一方で、SQLやシェル、HTMLといったセキュアな組み立てが必要な領域にはf-stringを使わず、パラメータバインディングやテンプレートエンジンを用いることが重要です。
既存コードの移行は段階的に行い、翻訳やロケールを考慮する箇所は従来手法や専用ライブラリを併用すると堅実です。
可読性と安全性のバランスを取りつつ、f-stringを日々の開発に取り入れていきましょう。