Pythonで文字列を結合・繰り返す方法:+、join、f文字列、*の使い方

Pythonで文字列を結合したり、同じ文字列を繰り返したりする方法はいくつもあります。

+演算子、str.join()、f文字列、*演算子はそれぞれ得意分野が少しずつ異なります。

この記事では、それぞれの使い方と使い分けのコツ、注意点や実用的な例、パフォーマンスまで丁寧に解説します。

Pythonの文字列結合・繰り返しの基本

どの方法をいつ使うか:+、join、f文字列、*

状況に応じて適切な手法を選ぶと、コードが読みやすく、速く、バグも減ります。

以下の表はおおまかな指針です。

手法主な用途書きやすさパフォーマンス
+ 演算子少数の文字列を手早く結合とても簡単小規模なら十分、ループでの多用は非推奨"Hello, " + name
str.join()多数要素を区切り付きで連結やや慣れが必要大量データに最適", ".join(items)
f文字列変数や式を読みやすく埋め込み小〜中規模で良好f"{user} has {n:,} pts"
* 演算子同じ文字列の繰り返し"-" * 80

区切り文字・改行・空白の扱いの基本

結合の成否は「どの区切りを入れるか」に左右されます。

スペースや改行の入れ忘れ・入れ過ぎはバグや見た目の崩れに直結します。

区切りが明確な場合はstr.join()を使うと意図を表現しやすく、改行は"\n"を使って明示的に制御します。

print()を使う場合はsepend引数で区切りや末尾の改行を調整できます。

また、余分な空白はstrip()rstrip()で取り除くと安定します。

Python
# 改行や空白の扱い
names = ["Alice", "Bob", "Carol"]
print(", ".join(names))           # 区切り: カンマ+スペース
print("\n".join(names))           # 区切り: 改行
print("A", "B", "C", sep="\t")    # printの区切りをタブに
print("line without newline", end="")  # 末尾改行なし
実行結果
Alice, Bob, Carol
Alice
Bob
Carol
A	B	C
line without newline

+演算子で文字列を結合する

+でシンプルに連結する使い方

+演算子は最も直感的で、小規模な結合に適しています。

スペースや句読点は文字列として明示的に入れます。

Python
# + で簡単に連結
first = "Python"
second = "3.12"
message = "Using " + first + " " + second + "!"
print(message)
実行結果
Using Python 3.12!

ヒント

インラインで小さな数の文字列を合わせるなら+で十分読みやすく、過剰な抽象化を避けられます。

複数回の+連結が遅くなるケースと対策

+でループしながら追記すると、都度新しい文字列が作られメモリコピーが発生します。

要素数が増えると二乗オーダーに近いコストになりやすく、パフォーマンス低下の原因になります。

対策は「リストに溜めて最後にjoin()」です。

Python
# 悪い例: + をループで使ってどんどん足す
def concat_plus(n: int) -> str:
    s = ""
    for i in range(n):
        s += str(i)  # 毎回新しい文字列を生成
    return s

# 良い例: 一旦リストに集めて join
def concat_join(n: int) -> str:
    parts = []
    for i in range(n):
        parts.append(str(i))
    return "".join(parts)

print(len(concat_plus(10_000)))
print(len(concat_join(10_000)))
実行結果
28940
28940

上記は機能的には同じ結果ですが、大きなnで実行時間が大きく違います。

str.join()でリストやイテラブルを結合する

joinの基本構文と区切り文字の指定

"区切り".join(イテラブル)の形で使います。

区切りが明確な時に最もわかりやすく効率的です。

Python
# joinの基本
items = ["spam", "eggs", "ham"]
csv = ",".join(items)
lines = "\n".join(items)
print(csv)
print("---")
print(lines)
実行結果
spam,eggs,ham
---
spam
eggs
ham

非文字列(数値・None)を結合する:map(str)の活用

join()は要素がすべて文字列である必要があります。

数値やNoneが混ざる場合は、map(str, iterable)や内包表記で文字列化、不要要素の除外を行います。

Python
# 数値や None を含む場合の対処
data = [1, 2, None, 4, 5]

# 1) すべてを文字列化(None -> "None" になる)
s1 = ", ".join(map(str, data))

# 2) None を除外して結合
s2 = ", ".join(str(x) for x in data if x is not None)

print(s1)
print(s2)
実行結果
1, 2, None, 4, 5
1, 2, 4, 5

大量データの結合にjoinが向く理由

join()は結合結果の総バイト数を先に見積もって、必要なメモリを一度だけ確保してからコピーします。

これにより、ループ中に断続的な再確保とコピーが発生する+連結に比べて格段に効率的になります。

ファイル出力や大きなログ文字列の組み立てなど、要素数が多い場面ではjoin()が基本になります。

f文字列で読みやすく結合する

f文字列の基本と式展開・フォーマット指定子

f文字列はリテラル内に{式}を書いて、その評価結果を直接埋め込めます。

桁区切りや小数点以下の桁数などのフォーマット指定も簡潔です(Python 3.6+)。

Python
# f文字列の基本
user = "Alice"
points = 1234567
ratio = 0.0789

msg = f"{user} has {points:,} points (ratio={ratio:.2%})"
print(msg)
実行結果
Alice has 1,234,567 points (ratio=7.89%)

補足

  • :, は3桁区切り
  • :.2% はパーセント表示(小数2桁)

f文字列とformat()の違い・使い分け

str.format()でも同様の書式化が可能ですが、f文字列の方が短く読みやすいコードになりやすいです。

一方でフォーマットテンプレートを事前に用意して複数回使い回す場合や翻訳文字列に適用する場合は、format()Templateの方が便利なこともあります。

Python
# 同等の処理を format() で
template = "{user} has {points:,} points (ratio={ratio:.2%})"
msg = template.format(user=user, points=points, ratio=ratio)
print(msg)
実行結果
Alice has 1,234,567 points (ratio=7.89%)
  • 単発の表示やログ行の構築: f文字列が読みやすい
  • テンプレートの再利用や国際化: format()string.Templateの利用を検討

*演算子で文字列を繰り返す

*で文字列をn回繰り返す基本

文字列 * nで繰り返しができます。

罫線やインデント、単純なパディング生成に向きます。

Python
# 基本的な繰り返し
print("-" * 10)
print("ab" * 3)
実行結果
----------
ababab

パディングや罫線の生成など実用例

繰り返しは、ヘッダーの下線や簡易テーブルの横罫線、インデントの生成など日常的に役立ちます。

Python
# 罫線やインデントの例
title = "Report"
line = "=" * len(title)
print(title)
print(line)

indent = " " * 4  # スペース4つのインデント
print(indent + "• item 1")
print(indent + "• item 2")
実行結果
Report
======
    • item 1
    • item 2

より厳密なパディングはljust/rjust/centerzfillも便利です。

Python
# 幅合わせ(桁埋め)
print("42".zfill(5))         # 0埋め
print("cat".rjust(6, "."))   # 右寄せ・ドット埋め
print("dog".center(7, "-"))  # 中央寄せ・ハイフン埋め
実行結果
00042
...cat
--dog--

使い分け・ベストプラクティスと注意点

パフォーマンス比較:+ vs join vs f文字列 vs *

あくまで一例ですが、以下のようにtimeitで比較できます(実行環境で数値は変わります)。

傾向として「多数要素の連結はjoin」「繰り返しは*」が速いです。

Python
# 簡易ベンチマーク(値は環境で変わります)
import timeit

setup = """
items = [str(i) for i in range(5000)]
"""

t_plus = timeit.timeit(
    stmt='"".join([s for s in items])'  # 参考として join の速さを見る
    , setup=setup, number=50)

t_bad_plus = timeit.timeit(
    stmt='''
s = ""
for x in items:
    s += x
''', setup=setup, number=10)

t_join = timeit.timeit(
    stmt='",".join(items)', setup=setup, number=1000)

t_f = timeit.timeit(
    stmt='f"{items[0]}-{items[1]}-{items[2]}"', setup=setup, number=500000)

t_mul = timeit.timeit(
    stmt='"-" * 1000', number=500000)

print("join (good pattern):", t_join)
print("+ in loop (bad pattern):", t_bad_plus)
print("f-string (small pieces):", t_f)
print("* repeat:", t_mul)
実行結果
join (good pattern): 0.12
+ in loop (bad pattern): 1.85
f-string (small pieces): 0.04
* repeat: 0.02

目安として、要素数が多い結合はjoin()、小規模な文字列合成はf文字列や+、繰り返しは*を選ぶと良いです。

改行・タブ・空文字の扱いとstrip/joinの併用

行末や先頭の余分な空白を取り除いてから結合すると、レイアウトが安定します。

タブや複数スペースを統一する際も、分割→整形→結合が有効です。

Python
# 前後の空白除去と改行での結合
raw_lines = ["  apple ", "", " banana", "cherry  "]
clean = [s.strip() for s in raw_lines if s.strip() != ""]
text = "\n".join(clean)
print(text)
実行結果
apple
banana
cherry

print()で区切りや末尾を制御する方法も安定運用に役立ちます。

Python
# printのsep/end
print("A", "B", "C", sep=" | ", end="\n\n")  # 区切りを明示、2重改行
print("done", end="")                         # 最後に改行しない
実行結果
A | B | C

done

型変換とエラー対策:str()、型アノテーション、Noneチェック

数値やNoneが混ざるとTypeErrorが出ます。

関数インターフェースの段階で型アノテーションを付け、早めにNoneチェックやstr()での明示変換を行うと安全です。

Python
from typing import Iterable, Optional

def join_safe(sep: str, items: Iterable[Optional[object]]) -> str:
    """
    None はスキップし、他は str() によって文字列化して結合する安全な関数。
    """
    filtered = (str(x) for x in items if x is not None)
    return sep.join(filtered)

print(join_safe(", ", [1, None, "x", 3.14]))
実行結果
1, x, 3.14

場合によってはNoneを許さず、早期に例外を投げるのが正しいこともあります。

Python
def must_join_strings(sep: str, items: Iterable[str]) -> str:
    for x in items:
        if not isinstance(x, str):
            raise TypeError("all items must be str")
    return sep.join(items)

try:
    print(must_join_strings(", ", ["ok", 1]))  # ここで TypeError
except Exception as e:
    print(repr(e))
実行結果
TypeError('all items must be str')

マルチバイト文字(日本語・絵文字)と繰り返しの注意点

Pythonの文字列はUnicodeであり、「文字数」と「表示幅」は一致しません。

全角文字は等幅フォントでも幅2として表示されることがあり、絵文字や合成シーケンス(例: 国旗 🇯🇵 は2コードポイント)ではさらに複雑です。

*で繰り返すこと自体は安全ですが、列幅そろえには注意が必要です。

表示幅で整列したい場合はwcwidthライブラリの利用が有効です。

Shellwcwidthインストールコマンド
pip install wcwidth
Python
# 表示幅に基づくパディング(wcwidthが必要)
# pip install wcwidth
from wcwidth import wcswidth

def pad_display_width(s: str, width: int, fill: str = " ") -> str:
    """
    表示幅がwidthになるよう右側をfillで埋める。
    """
    w = wcswidth(s)
    if w < 0:
        # 測定不能な場合はそのまま返す
        return s
    return s + (fill * max(0, width - w))

items = ["abc", "こんにちは", "🍣", "🇯🇵JP"]
for x in items:
    print(pad_display_width(x, 10, ".") + "|")
実行結果
abc.......|
こんにちは|
🍣........|
🇯🇵JP......|

上記のように、コードポイント数ではなく表示幅に基づく調整で見た目が整います。

コンソールやフォントによって幅の扱いが異なる点も覚えておくと良いでしょう。

まとめ

  • 少数の結合は+やf文字列で簡潔に書けます。読みやすさ重視ならf文字列が便利です。
  • 多数要素や区切り付きの結合はstr.join()が基本で、高速かつメモリ効率に優れます。
  • 同じ文字列の繰り返しは*が最短・最速です。罫線やインデント、簡単なパディングに活用できます。
  • 改行や空白は明示的に扱い、strip()print(sep=..., end=...)で制御すると安定します。
  • 非文字列を混ぜるときはstr()map(str)Noneの扱い方針(除外・明示文字列化・例外)を決めておきましょう。
  • 日本語・絵文字の整列は表示幅の問題があるため、必要に応じてwcwidthなどで幅を測ると安心です。

これらの原則とテクニックを押さえておけば、文字列の結合・繰り返しは性能と可読性の双方で安定したコードに仕上がります。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!