Pythonのf-stringは、読みやすく書きやすい文字列フォーマットとして多くの現場で使われています。
一方で、細かな書式指定やNGパターンを正確に押さえておかないと、思わぬバグや可読性の低下を招いてしまいます。
本記事では、実務でよく使うf-stringの書式とテクニック、そして避けるべき書き方を体系的に整理して解説します。
f-stringとは
f-stringの基本構文とメリット

f-string(フォーマット済み文字列リテラル)とは、文字列の中に{式}を書くだけで、その評価結果を埋め込めるPythonの機能です。
プレフィックスとしてfまたはFを付けて使います。
name = "Alice"
age = 30
# f-stringの基本
message = f"My name is {name}, I am {age} years old."
print(message)
My name is Alice, I am 30 years old.
f-stringを使うと、次のようなメリットがあります。
- 変数や式を
{}内に直接書けるため、可読性が高いです。 - 書式指定(ゼロ埋め、桁区切り、小数点桁数など)をコンパクトに記述できます。
- 実行時に評価されるため、任意の式(四則演算、メソッド呼び出しなど)も埋め込めます。
「文字列中に式を書いて、その場で評価結果を埋め込む」、これがf-stringの本質です。
従来のformatとの違い

従来はstr.format()や%演算子でフォーマットしていました。
例えば次のような書き方です。
name = "Alice"
score = 95.5
# 従来のformat
msg1 = "Name: {}, Score: {:.1f}".format(name, score)
# f-string
msg2 = f"Name: {name}, Score: {score:.1f}"
print(msg1)
print(msg2)
Name: Alice, Score: 95.5
Name: Alice, Score: 95.5
機能的にはほぼ同じことができますが、f-stringには次の違いがあります。
- どの値がどの位置に入るかが、一目でわかりやすいです。
- 位置引数・キーワード引数を並べる必要がないため、記述ミスが減ります。
{x + y}のように、式をそのまま書ける点が大きな特徴です。
一方で、f-stringは実行時に式を評価するため、ユーザー入力など危険な値をそのまま埋め込むとセキュリティリスクになり得る点に注意が必要です(後述します)。
Pythonバージョン要件
f-stringが使えるのはPython 3.6以降です。
Python 3.5以前では構文エラーになります。
以下のようなポイントを押さえておきます。
- Python 3.6〜3.7: 基本的なf-string機能が利用可能です。
- Python 3.8以降:
=付きの「デバッグ用f-string」(f"{var=}")が追加されています。 - Python 2系では一切使えません。
x = 10
y = 20
# Python 3.8以降で使えるデバッグ用f-string
print(f"{x=}, {y=}, {x + y=}")
x=10, y=20, x + y=30
Python 3.8未満の環境にデプロイする場合は、この{var=}構文は避ける必要があります。
f-string書式一覧
ここからは、f-stringで使える主な書式指定をカテゴリごとに整理します。
基本形は次の通りです。
f"{式:フォーマット指定}"
:の後ろに、数値や日付の見た目を変えるための指定を書きます。
文字列フォーマット

文字列に対しては、主に「幅」「寄せ方向」「切り詰め」などを指定します。
代表的な書式を表にまとめます。
| 書式例 | 意味 | 出力例(入力: “abc”) |
|---|---|---|
{s:10} | 右寄せ(デフォルト)、幅10 | " abc" |
{s:<10} | 左寄せ、幅10 | "abc " |
{s:^10} | 中央寄せ、幅10 | " abc " |
{s:.2} | 最大2文字まで | "ab" |
{s:*>10} | 幅10、右寄せ、空きは*で埋める | "*******abc" |
s = "abc"
print(f"右寄せ: [{s:10}]")
print(f"左寄せ: [{s:<10}]")
print(f"中央寄せ: [{s:^10}]")
print(f"長さ制限: [{s:.2}]")
print(f"埋め文字変更: [{s:*>10}]")
右寄せ: [ abc]
左寄せ: [abc ]
中央寄せ: [ abc ]
長さ制限: [ab]
埋め文字変更: [*******abc]
幅指定と寄せ方向は、ラベルや表形式の出力を整える際に非常に有用です。
数値フォーマット

整数のフォーマットでは、主に「幅」「ゼロ埋め」「符号」「桁区切り」を指定します。
| 書式例 | 説明 | 入力: 42 | 出力 |
|---|---|---|---|
{n:d} | 10進数(デフォルト) | 42 | 42 |
{n:5d} | 幅5で右寄せ | 42 | 42 |
{n:05d} | 幅5でゼロ埋め | 42 | 00042 |
{n:+d} | 正数も符号付き | 42 | +42 |
{n:=+5d} | 符号を左に固定してゼロ埋め | 42 | +0042 |
{n:,} | 3桁ごとにカンマ区切り | 1234567 | 1,234,567 |
n = 42
big = 1234567
print(f"10進数: {n:d}")
print(f"幅5 右寄せ: [{n:5d}]")
print(f"幅5 ゼロ埋め: [{n:05d}]")
print(f"符号付き: {n:+d}")
print(f"符号固定+ゼロ埋め: [{n:=+5d}]")
print(f"3桁区切り: {big:,}")
10進数: 42
幅5 右寄せ: [ 42]
幅5 ゼロ埋め: [00042]
符号付き: +42
符号固定+ゼロ埋め: [+0042]
3桁区切り: 1,234,567
数値の桁揃えやゼロ埋めは、IDや連番、レポート出力などで頻出です。
浮動小数点フォーマット

浮動小数点では「小数点以下の桁数」「有効数字」「指数表記」などをコントロールします。
| 書式 | 説明 | 入力: 123.456 |
|---|---|---|
{x:f} | 固定小数点(デフォルト6桁) | 123.456000 |
{x:.2f} | 小数点以下2桁(四捨五入) | 123.46 |
{x:8.2f} | 幅8、小数点以下2桁 | 123.46 |
{x:g} | 有効数字(デフォルト) | 123.456 |
{x:.3g} | 有効数字3桁 | 123 |
{x:e} | 指数表記 | 1.234560e+02 |
x = 123.456
print(f"固定小数点(デフォルト): {x:f}")
print(f"小数点以下2桁: {x:.2f}")
print(f"幅8かつ小数2桁: [{x:8.2f}]")
print(f"有効数字(デフォルト): {x:g}")
print(f"有効数字3桁: {x:.3g}")
print(f"指数表記: {x:e}")
固定小数点(デフォルト): 123.456000
小数点以下2桁: 123.46
幅8かつ小数2桁: [ 123.46]
有効数字(デフォルト): 123.456
有効数字3桁: 123
指数表記: 1.234560e+02
金融系などで「必ず小数点以下2桁」といった要件がある場合は.2fが定番です。
パーセント表記

%フォーマットを使うと、0〜1の値をパーセント表示できます。
rate = 0.1234
print(f"デフォルト%: {rate:%}")
print(f"小数2桁%: {rate:.2%}")
print(f"幅8かつ小数1桁: [{rate:8.1%}]")
デフォルト%: 12.340000%
小数2桁%: 12.34%
幅8かつ小数1桁: [ 12.3%]
内部的には値が100倍され、その後に%が付与される点を意識しておくと、丸め方をイメージしやすくなります。
16進数・2進数などの進数変換

整数は、f-stringで簡単に別の進数表記に変換できます。
| 書式 | 説明 | 入力: 255 | 出力 |
|---|---|---|---|
{n:b} | 2進数 | 11111111 | |
{n:o} | 8進数 | 377 | |
{n:x} | 16進数(小文字) | ff | |
{n:X} | 16進数(大文字) | FF | |
{n:#x} | 16進数+接頭辞0x | 0xff | |
{n:#b} | 2進数+接頭辞0b | 0b11111111 |
n = 255
print(f"2進数: {n:b}")
print(f"8進数: {n:o}")
print(f"16進数(小文字): {n:x}")
print(f"16進数(大文字): {n:X}")
print(f"接頭辞付き16進数: {n:#x}")
print(f"接頭辞付き2進数: {n:#b}")
2進数: 11111111
8進数: 377
16進数(小文字): ff
16進数(大文字): FF
接頭辞付き16進数: 0xff
接頭辞付き2進数: 0b11111111
ビット演算結果の確認やデバッグなどで2進数・16進数表示は頻出です。
日付・時刻のフォーマット

日付や時刻は、f-stringそのものではなくdatetime.strftime()の書式指定を組み合わせて使います。
from datetime import datetime
now = datetime(2024, 5, 1, 14, 30, 45)
print(f"ISO風: {now:%Y-%m-%d %H:%M:%S}")
print(f"日付のみ: {now:%Y/%m/%d}")
print(f"時刻のみ: {now:%H:%M}")
print(f"和風表記: {now:%Y年%m月%d日(%a) %H時%M分}")
ISO風: 2024-05-01 14:30:45
日付のみ: 2024/05/01
時刻のみ: 14:30
和風表記: 2024年05月01日(Wed) 14時30分
| 指定子 | 意味 | 例 |
|---|---|---|
%Y | 西暦4桁 | 2024 |
%m | 月(ゼロ埋め2桁) | 05 |
%d | 日(ゼロ埋め2桁) | 01 |
%H | 時(24時間) | 14 |
%M | 分 | 30 |
%S | 秒 | 45 |
%a | 曜日(短縮名) | Wed |
f-stringの{now:%Y-%m-%d}のような書き方は、実はnow.__format__()が呼ばれているという点も押さえておくと理解が深まります。
f-stringのよく使うパターン
変数名と値を同時に表示

Python 3.8以降では、変数名と値を同時に出力できる{var=}構文が利用できます。
デバッグで非常に便利です。
x = 10
y = 20
z = x * y
# 3.8以降で使える
print(f"{x=}, {y=}, {z=}")
x=10, y=20, z=200
3.7以前との互換性が必要な場合は、明示的に名前を書きます。
print(f"x={x}, y={y}, z={z}")
辞書・リストを使ったf-string活用

f-stringの{}内には式を書けるので、辞書やリストから直接値を取り出せます。
user = {"name": "Alice", "age": 30}
scores = [80, 90, 100]
print(f"ユーザー: {user['name']} ({user['age']}歳)")
print(f"最初のスコア: {scores[0]}")
print(f"平均スコア: {sum(scores) / len(scores):.1f}")
ユーザー: Alice (30歳)
最初のスコア: 80
平均スコア: 90.0
辞書キーやリストインデックスへのアクセスを、そのまま文字列中に書けるため、テンプレートエンジンのような感覚で使えます。
インデックス・キー参照を含むf-string

もう少し複雑な構造でも、そのままチェーンしてアクセスできます。
config = {
"db": {
"host": "localhost",
"port": 5432,
},
"debug": True,
}
print(f"DB: {config['db']['host']}:{config['db']['port']} (debug={config['debug']})")
DB: localhost:5432 (debug=True)
ただし、あまりにも長い式をf-stringの中に書くと、可読性が下がりやすいです。
複雑なアクセスは一旦変数に代入してから使うと読みやすくなります。
条件式と三項演算子を使ったf-string

三項演算子を使うと、条件に応じて埋め込む文字列を切り替えられます。
score = 75
status = "OK" if score >= 60 else "NG"
print(f"点数: {score} ({'合格' if score >= 60 else '不合格'})")
print(f"ステータス: {status}")
点数: 75 (合格)
ステータス: OK
if文そのものは{}内に直接は書けませんが、式としての条件演算は利用できます。
ネストしたフォーマット指定の書き方

幅や精度を変数で動的に指定したい場合、{式:{幅}.{精度}f}のようなネストした使い方ができます。
value = 12.3456
width = 8
precision = 3
# 幅と精度を変数で指定
print(f"幅={width}, 精度={precision}: [{value:{width}.{precision}f}]")
幅=8, 精度=3: [ 12.346]
さらに、「内部で一度フォーマットしてから、外側でもう一度フォーマットする」といったことも可能ですが、過度なネストは読みにくくなるため、必要最小限にとどめるのがおすすめです。
複数行f-string

f-stringは"""..."""を使って複数行の文字列としても利用できます。
テンプレート的な用途で便利です。
title = "レポート"
value = 123.456
report = f"""
=== {title} ===
値: {value:.2f}
2倍: {value * 2:.2f}
"""
print(report)
=== レポート ===
値: 123.46
2倍: 246.91
インデントを整えたい場合はtextwrap.dedentを併用することも多いです。
import textwrap
title = "レポート"
value = 123.456
report = f"""
=== {title} ===
値: {value:.2f}
2倍: {value * 2:.2f}
"""
print(textwrap.dedent(report))
=== レポート ===
値: 123.46
2倍: 246.91
f-stringのNG例と注意点
バックスラッシュを含むf-stringの注意点

f-stringの中で\(バックスラッシュ)を含むパスなどを書くと、意図しないエスケープシーケンスになってしまうことがあります。
name = "data.txt"
# NG例: \t がタブとして解釈される
path = f"C:\temp\{name}"
print(path)
このコードはC: emp\data.txtのように、\tがタブに変換されてしまいます。
回避策としては、次のような方法があります。
name = "data.txt"
# 方法1: バックスラッシュをエスケープする
path1 = f"C:\\temp\\{name}"
# 方法2: os.path.joinを使う(推奨)
import os
path2 = os.path.join("C:", "temp", name)
# 方法3: Raw文字列とf-stringを分ける(Python 3.12以前では f-r は制約が多い)
path3 = r"C:\temp" + "\\" + name # もしくは pathlib.Path を使う
print(path1)
print(path2)
print(path3)
C:\temp\data.txt
C:\temp\data.txt
C:\temp\data.txt
Windowsパスをf-stringに直書きするのはバグの温床になりやすいので、os.pathやpathlibの利用を検討するのが安全です。
ブレース{}のエスケープ忘れによるエラー

f-stringでは{}が式の区切りとして使われているため、波かっこそのものを出したい場合はエスケープが必要です。
x = 10
# NG: 構文エラーになる
# text = f"値は {x}, プレースホルダは {value} です"
# OK: {{ と }} で { と } を表す
text = f"値は {x}, プレースホルダは {{value}} です"
print(text)
値は 10, プレースホルダは {value} です
テンプレート文字列や、フォーマット用のサンプルを表示したいときなど、エスケープを忘れるとSyntaxErrorになるので注意が必要です。
複雑なロジックをf-string内に書きすぎるNG例

f-stringの{}内には任意の式が書けるからといって、なんでも詰め込むと可読性が急激に悪化します。
user = {"name": "Alice", "age": 30, "tags": ["admin", "editor"]}
# NG例: 複雑すぎる
print(
f"Name={user['name'].upper()}, "
f"Age={user['age']}, "
f"Role={','.join(t for t in user['tags'] if t != 'editor') or 'none'}"
)
改善例として、ロジックは事前に変数に分けると読みやすくなります。
user = {"name": "Alice", "age": 30, "tags": ["admin", "editor"]}
name_upper = user["name"].upper()
age = user["age"]
roles = ",".join(t for t in user["tags"] if t != "editor") or "none"
print(f"Name={name_upper}, Age={age}, Role={roles}")
Name=ALICE, Age=30, Role=admin
「f-stringはあくまで表示の最終段階、その前に必要な計算は済ませておく」という設計にすると、コードがぐっと読みやすくなります。
ロケール依存の数値フォーマットの落とし穴

Pythonの標準的な{value:,}のようなフォーマットは、基本的にはロケール非依存で、常に,が3桁区切り、小数点は.です。
しかし、localeモジュールと組み合わせてフォーマットすると、環境によって桁区切りや小数点の表記が変わる可能性があります。
import locale
value = 1234.56
# ロケールに依存しない: f-stringの標準フォーマット
print(f"標準: {value:,.2f}")
# ロケール依存: locale.format_string を使う例
locale.setlocale(locale.LC_ALL, "") # 環境のロケールに依存
formatted = locale.format_string("%.2f", value, grouping=True)
print(f"ロケール依存: {formatted}")
標準: 1,234.56
ロケール依存: (環境により異なる)
国際化対応が必要な場合は、ロケール依存かどうかを明示的に決めて設計することが重要です。
日本向けであれば、{value:,.0f}など、ロケール非依存のフォーマットを使うほうが予測しやすくなります。
セキュリティ

f-stringは便利ですが、ユーザー入力や外部からの値をそのままコードやSQLに埋め込むと深刻な脆弱性につながります。
user_input = "1; DROP TABLE users;" # 例
# NG: SQLインジェクションの危険
query = f"SELECT * FROM users WHERE id = {user_input}"
print(query)
SELECT * FROM users WHERE id = 1; DROP TABLE users;
データベース操作では、必ずパラメータバインドを使います。
import sqlite3
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
user_input = "1; DROP TABLE users;"
# OK: プレースホルダとパラメータで安全に扱う
safe_query = "SELECT * FROM users WHERE id = ?"
cursor.execute(safe_query, (user_input,))
また、evalやexecとf-stringを組み合わせるのも極めて危険です。
外部入力を含む文字列をコードとして実行しない、という原則を徹底してください。
パフォーマンス観点で避けたい書き方

f-string自体は高速ですが、大量の文字列連結をループの中で行うとパフォーマンス低下を招くことがあります。
# NG: ループ内で逐次連結
result = ""
for i in range(10000):
result += f"{i},"
このような書き方は、文字列が不変であるPythonでは毎回新しい文字列オブジェクトを生成するため、効率が良くありません。
改善策として、リストに貯めて最後にjoinするのが定石です。
# OK: リストに貯めて最後にjoin
parts = []
for i in range(10000):
parts.append(f"{i}") # f-string自体はOK
result = ",".join(parts)
あるいは、生成式を直接joinに渡す方法もあります。
result = ",".join(f"{i}" for i in range(10000))
「ループのたびに巨大な文字列を作り直していないか」という観点で、f-stringの使い方を見直すと、パフォーマンス問題を避けやすくなります。
まとめ
f-stringは、Pythonで最も実務的な文字列フォーマット手段と言ってよいほど強力で便利な機能です。
本記事では、基本構文から文字列・数値・日付の書式一覧、よく使うパターン、そしてNG例やセキュリティ・パフォーマンス上の注意点まで幅広く解説しました。
日常的なログ出力やレポート生成はもちろん、デバッグやテンプレート的な用途にも応用できます。
ここで紹介したパターンを押さえておけば、f-stringを安全かつ効果的に使いこなせるはずです。
