Pythonリストを結合する|+演算子とextendメソッドの違い

Pythonリスト結合の基本から応用まで完全解説。+演算子とextendメソッドの違いとパフォーマンス比較を詳しく解説。

Learning Next 運営
29 分で読めます

みなさん、Pythonでリストを結合する時に「どの方法を使えばいいんだろう?」と迷ったことはありませんか?

「+演算子とextendメソッドって何が違うの?」 「どっちの方が速いの?」 「使い分けのポイントがわからない」

そんな疑問を抱えている方も多いはず。

この記事では、Pythonでリストを結合する主要な方法である+演算子とextendメソッドの違いを詳しく解説します。 具体的なコード例とともに使い分けのポイントを説明するので、リスト結合の最適な方法をマスターできますよ!

+演算子でリストを結合

まず、最もシンプルな+演算子を使ったリスト結合について見ていきましょう。 初心者の方にも分かりやすい方法です。

基本的な使い方

+演算子の最大の特徴は、新しいリストを作成することです。

# +演算子の基本的な使い方
list1 = [1, 2, 3]
list2 = [4, 5, 6]
result = list1 + list2
print(f"list1: {list1}")
print(f"list2: {list2}")
print(f"結合結果: {result}")
print(f"元のリスト1: {list1}") # 元のリストは変更されない
print(f"元のリスト2: {list2}") # 元のリストは変更されない

実行結果:

list1: [1, 2, 3] list2: [4, 5, 6] 結合結果: [1, 2, 3, 4, 5, 6] 元のリスト1: [1, 2, 3] 元のリスト2: [1, 2, 3]

重要なポイントは、元のリストが一切変更されないことです。 新しいリストが作られるんですね。

複数のリストも一度に結合できます。

# 複数のリストの結合
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
multiple_result = list1 + list2 + list3
print(f"3つのリスト結合: {multiple_result}")
# 空リストとの結合も可能
empty_list = []
empty_result = list1 + empty_list
print(f"空リストとの結合: {empty_result}")

実行結果:

3つのリスト結合: [1, 2, 3, 4, 5, 6, 7, 8, 9] 空リストとの結合: [1, 2, 3]

とても直感的で分かりやすいですね。

様々なデータ型での結合

+演算子は、様々なデータ型のリストでも使えます。

# 文字列リストの結合
fruits1 = ["apple", "banana"]
fruits2 = ["orange", "grape"]
all_fruits = fruits1 + fruits2
print(f"果物リスト: {all_fruits}")
# 異なる型の要素を含むリスト
numbers = [1, 2, 3]
mixed = ["hello", 4.5, True]
combined = numbers + mixed
print(f"混合リスト: {combined}")
# ネストしたリストの結合
nested1 = [[1, 2], [3, 4]]
nested2 = [[5, 6], [7, 8]]
nested_result = nested1 + nested2
print(f"ネストリスト: {nested_result}")

実行結果:

果物リスト: ['apple', 'banana', 'orange', 'grape'] 混合リスト: [1, 2, 3, 'hello', 4.5, True] ネストリスト: [[1, 2], [3, 4], [5, 6], [7, 8]]

ただし、注意点もあります。

# タプルとの結合(エラーになる例)
try:
numbers = [1, 2, 3]
tuple_data = (1, 2, 3)
error_result = numbers + tuple_data
except TypeError as e:
print(f"タプルとの結合エラー: {e}")

リスト同士でないと+演算子は使えません。 型が違うとエラーになるので注意しましょう。

extendメソッドでリストを結合

続いて、元のリストを変更するextendメソッドについて説明します。 +演算子とは動作が大きく異なります。

基本的な使い方

extendメソッドの特徴は、元のリストに直接要素を追加することです。

# extendメソッドの基本使用法
original_list = [1, 2, 3]
extension_list = [4, 5, 6]
print(f"結合前: {original_list}")
print(f"追加リスト: {extension_list}")
# extendで結合(元のリストが変更される)
original_list.extend(extension_list)
print(f"結合後: {original_list}")
# 戻り値はNone
result = original_list.extend([7, 8])
print(f"extendの戻り値: {result}")
print(f"変更されたリスト: {original_list}")

実行結果:

結合前: [1, 2, 3] 追加リスト: [4, 5, 6] 結合後: [1, 2, 3, 4, 5, 6] extendの戻り値: None 変更されたリスト: [1, 2, 3, 4, 5, 6, 7, 8]

extendメソッドは、元のリストを直接変更します。 戻り値はNoneなので注意が必要ですね。

複数回のextendも可能です。

# 複数回のextend
numbers = [1, 2]
numbers.extend([3, 4])
numbers.extend([5, 6])
print(f"複数回extend: {numbers}")

実行結果:

複数回extend: [1, 2, 3, 4, 5, 6]

イテラブルオブジェクトとの結合

extendメソッドの便利な点は、リスト以外のイテラブルオブジェクトも扱えることです。

# 文字列をextend(1文字ずつ追加される)
letters = ['a', 'b']
letters.extend("cde")
print(f"文字列をextend: {letters}")
# タプルをextend
numbers = [1, 2]
numbers.extend((3, 4, 5))
print(f"タプルをextend: {numbers}")
# rangeオブジェクトをextend
base_list = [0]
base_list.extend(range(1, 6))
print(f"rangeをextend: {base_list}")

実行結果:

文字列をextend: ['a', 'b', 'c', 'd', 'e'] タプルをextend: [1, 2, 3, 4, 5] rangeをextend: [0, 1, 2, 3, 4, 5]

文字列は1文字ずつ追加されるのが面白いですね。

ジェネレータも使えます。

# ジェネレータをextend
squares = [1, 4]
squares.extend(x**2 for x in range(3, 6))
print(f"ジェネレータをextend: {squares}")
# ネストしたリストをextend
nested = [[1, 2]]
nested.extend([[3, 4], [5, 6]])
print(f"ネストリストをextend: {nested}")

実行結果:

ジェネレータをextend: [1, 4, 9, 16, 25] ネストリストをextend: [[1, 2], [3, 4], [5, 6]]

extendは様々なデータ型に対応していて、とても柔軟ですね。

+演算子とextendの違いを比較

両手法の違いと使い分けのポイントを詳しく見てみましょう。 この理解が重要です。

機能的な違い

最も重要な違いを確認してみましょう。

# 元のリストへの影響の違い
print("1. 元のリストへの影響:")
original1 = [1, 2, 3]
original2 = [1, 2, 3]
addition_list = [4, 5, 6]
# +演算子(新しいリストを作成)
result_plus = original1 + addition_list
print(f" +演算子後の元リスト: {original1}")
print(f" +演算子の結果: {result_plus}")
# extendメソッド(元のリストを変更)
original2.extend(addition_list)
print(f" extend後の元リスト: {original2}")

実行結果:

1. 元のリストへの影響: +演算子後の元リスト: [1, 2, 3] +演算子の結果: [1, 2, 3, 4, 5, 6] extend後の元リスト: [1, 2, 3, 4, 5, 6]

戻り値の違いも重要です。

# 戻り値の違い
print("2. 戻り値の違い:")
list_a = [1, 2]
list_b = [3, 4]
plus_return = list_a + list_b
extend_return = list_a.extend(list_b)
print(f" +演算子の戻り値: {plus_return}")
print(f" extendの戻り値: {extend_return}")

実行結果:

2. 戻り値の違い: +演算子の戻り値: [1, 2, 3, 4] extendの戻り値: None

メソッドチェーンでの使用にも違いがあります。

# メソッドチェーンでの使用
print("3. メソッドチェーン:")
# +演算子はチェーン可能
chain_result = [1] + [2] + [3] + [4]
print(f" +演算子チェーン: {chain_result}")
# extendはチェーン不可(戻り値がNone)
print(" extendはチェーン不可(戻り値がNone)")

+演算子なら連続して使えますが、extendは戻り値がNoneなのでチェーンできません。

パフォーマンスの比較

実際にどちらが速いか測定してみましょう。

import time
# パフォーマンス比較
print("=== パフォーマンス比較 ===")
# テスト用データの準備
base_list_size = 10000
addition_size = 5000
# +演算子のパフォーマンステスト
print("1. +演算子のパフォーマンス:")
base_list = list(range(base_list_size))
addition_list = list(range(addition_size))
start_time = time.time()
result_plus = base_list + addition_list
plus_time = time.time() - start_time
print(f" 実行時間: {plus_time:.6f}秒")
print(f" 結果リスト長: {len(result_plus)}")
print(f" 元リスト変更: なし")
# extendメソッドのパフォーマンステスト
print(f"
2. extendメソッドのパフォーマンス:")
base_list_copy = list(range(base_list_size))
start_time = time.time()
base_list_copy.extend(addition_list)
extend_time = time.time() - start_time
print(f" 実行時間: {extend_time:.6f}秒")
print(f" 結果リスト長: {len(base_list_copy)}")
print(f" 元リスト変更: あり")

実行結果の例:

=== パフォーマンス比較 === 1. +演算子のパフォーマンス: 実行時間: 0.000234秒 結果リスト長: 15000 元リスト変更: なし 2. extendメソッドのパフォーマンス: 実行時間: 0.000156秒 結果リスト長: 15000 元リスト変更: あり

一般的に、extendメソッドの方が高速です。 新しいリストを作成する必要がないからですね。

# パフォーマンス比較結果
print(f"3. 比較結果:")
if extend_time < plus_time:
speedup = plus_time / extend_time
print(f" extendが約{speedup:.2f}倍高速")
else:
speedup = extend_time / plus_time
print(f" +演算子が約{speedup:.2f}倍高速")
# メモリ使用量の考察
print(f"
4. メモリ使用量:")
print(" +演算子: 新しいリストを作成(追加メモリ必要)")
print(" extend: 元リストを拡張(メモリ効率的)")

使い分けのポイント

実際の開発では、どちらを使うべきでしょうか? 状況に応じた使い分けをご説明しますね。

+演算子を使うべき場面

元のリストを保持したい場合に+演算子が適しています。

# +演算子を使うべき場面
# 1. 元のリストを保持したい場合
print("1. 元のリストを保持したい場合:")
original_data = [1, 2, 3, 4, 5]
filter_values = [6, 7, 8]
# 条件付きで結合(元データは保持)
if len(filter_values) > 0:
combined_data = original_data + filter_values
print(f" 元データ: {original_data}")
print(f" 結合データ: {combined_data}")
# 2. 一時的な結合が必要な場合
print(f"
2. 一時的な結合:")
def process_combined_list(list1, list2):
temp_list = list1 + list2
return sum(temp_list)
result = process_combined_list([1, 2], [3, 4])
print(f" 処理結果: {result}")

実行結果:

1. 元のリストを保持したい場合: 元データ: [1, 2, 3, 4, 5] 結合データ: [1, 2, 3, 4, 5, 6, 7, 8] 2. 一時的な結合: 処理結果: 10

複数のリストを一度に結合する場合も+演算子が便利です。

# 3. 複数リストの一括結合
print(f"3. 複数リストの一括結合:")
morning_tasks = ["朝食", "歯磨き"]
work_tasks = ["会議", "コーディング", "レビュー"]
evening_tasks = ["夕食", "読書"]
all_tasks = morning_tasks + work_tasks + evening_tasks
print(f" 1日のタスク: {all_tasks}")
# 4. 関数型プログラミングスタイル
print(f"
4. 関数型スタイル:")
def combine_and_process(base, additions):
return sorted(base + additions)
numbers1 = [3, 1, 4]
numbers2 = [2, 5, 0]
sorted_result = combine_and_process(numbers1, numbers2)
print(f" ソート結果: {sorted_result}")

実行結果:

3. 複数リストの一括結合: 1日のタスク: ['朝食', '歯磨き', '会議', 'コーディング', 'レビュー', '夕食', '読書'] 4. 関数型スタイル: ソート結果: [0, 1, 2, 3, 4, 5]

extendメソッドを使うべき場面

メモリ効率とパフォーマンス重視の場合にextendが適しています。

# extendメソッドを使うべき場面
# 1. 段階的にデータを蓄積する場合
print("1. 段階的なデータ蓄積:")
collected_data = []
# 複数の処理からデータを収集
for i in range(3):
batch_data = [f"item_{i}_{j}" for j in range(3)]
collected_data.extend(batch_data)
print(f" バッチ{i+1}後: {collected_data}")

実行結果:

1. 段階的なデータ蓄積: バッチ1後: ['item_0_0', 'item_0_1', 'item_0_2'] バッチ2後: ['item_0_0', 'item_0_1', 'item_0_2', 'item_1_0', 'item_1_1', 'item_1_2'] バッチ3後: ['item_0_0', 'item_0_1', 'item_0_2', 'item_1_0', 'item_1_1', 'item_1_2', 'item_2_0', 'item_2_1', 'item_2_2']

ファイル読み込みなどでも有効です。

# 2. ファイル読み込み風の処理
print(f"2. ファイル読み込み風の処理:")
all_lines = []
# 複数ファイルからの行を想定
file_contents = [
["line1", "line2"],
["line3", "line4", "line5"],
["line6"]
]
for file_lines in file_contents:
all_lines.extend(file_lines)
print(f" 全行: {all_lines}")
# 3. 動的配列の構築
print(f"
3. 動的配列の構築:")
result_list = [0] # 初期値
# 条件に応じて要素を追加
conditions = [True, False, True, True]
for i, condition in enumerate(conditions, 1):
if condition:
result_list.extend([i * 10, i * 10 + 1])
print(f" 動的構築結果: {result_list}")

実行結果:

2. ファイル読み込み風の処理: 全行: ['line1', 'line2', 'line3', 'line4', 'line5', 'line6'] 3. 動的配列の構築: 動的構築結果: [0, 10, 11, 30, 31, 40, 41]

ジェネレータの活用でもextendが力を発揮します。

# 4. ジェネレータの活用
print(f"4. ジェネレータの活用:")
def number_generator(start, count):
for i in range(count):
yield start + i
final_numbers = [100]
final_numbers.extend(number_generator(200, 5))
final_numbers.extend(number_generator(300, 3))
print(f" ジェネレータ結合: {final_numbers}")

実行結果:

4. ジェネレータの活用: ジェネレータ結合: [100, 200, 201, 202, 203, 204, 300, 301, 302]

その他のリスト結合方法

+演算子とextend以外にも、便利な結合方法があります。 モダンなPythonの書き方もご紹介しますね。

unpack演算子とリスト内包表記

Python 3.5以降で使えるunpack演算子は、とても便利です。

# その他のリスト結合方法
# 1. unpack演算子(*)
print("1. unpack演算子(*):")
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
unpacked_result = [*list1, *list2, *list3]
print(f" unpack結合: {unpacked_result}")
# 2. リスト内包表記
print(f"
2. リスト内包表記:")
lists_to_combine = [[1, 2], [3, 4], [5, 6]]
comprehension_result = [item for sublist in lists_to_combine for item in sublist]
print(f" 内包表記結合: {comprehension_result}")
# 3. itertools.chainを使った結合
print(f"
3. itertools.chain:")
import itertools
chain_result = list(itertools.chain(list1, list2, list3))
print(f" chain結合: {chain_result}")

実行結果:

1. unpack演算子(*): unpack結合: [1, 2, 3, 4, 5, 6, 7, 8, 9] 2. リスト内包表記: 内包表記結合: [1, 2, 3, 4, 5, 6] 3. itertools.chain: chain結合: [1, 2, 3, 4, 5, 6, 7, 8, 9]

sum関数を使う方法もありますが、パフォーマンスが悪いので注意が必要です。

# 4. sum関数を使った結合(非推奨)
print(f"4. sum関数(非推奨):")
nested_lists = [[1, 2], [3, 4], [5, 6]]
sum_result = sum(nested_lists, [])
print(f" sum結合: {sum_result}")
print(" ※パフォーマンスが悪いため非推奨")
# 5. 方法別の特徴まとめ
print(f"
5. 方法別の特徴:")
print(" +演算子: シンプル、新リスト作成")
print(" extend: 高速、元リスト変更")
print(" unpack: モダン、可読性高い")
print(" chain: イテレータ、メモリ効率的")

それぞれに特徴があるので、用途に応じて選びましょう。

使い分けの判断基準

最適な結合方法を選ぶための判断基準をまとめてみましょう。 これがあれば迷うことはありません。

判断フローチャート

状況に応じた選択方法をコードで表現してみました。

# 使い分けの判断基準
def choose_concatenation_method(preserve_original=True,
performance_critical=False,
multiple_lists=False):
"""最適な結合方法を提案する"""
if preserve_original:
if multiple_lists:
return "unpack演算子 [*list1, *list2, *list3]"
else:
return "+演算子"
else:
if performance_critical:
return "extendメソッド"
else:
return "extend または +演算子"
# 各シナリオでの推奨方法
scenarios = [
{"name": "元リスト保持、2つのリスト",
"params": {"preserve_original": True, "multiple_lists": False}},
{"name": "元リスト変更OK、高速処理",
"params": {"preserve_original": False, "performance_critical": True}},
{"name": "元リスト保持、複数リスト",
"params": {"preserve_original": True, "multiple_lists": True}},
{"name": "元リスト変更OK、通常処理",
"params": {"preserve_original": False, "performance_critical": False}},
]
print("=== リスト結合方法の選び方 ===")
for scenario in scenarios:
method = choose_concatenation_method(**scenario["params"])
print(f" {scenario['name']}: {method}")

実行結果:

=== リスト結合方法の選び方 === 元リスト保持、2つのリスト: +演算子 元リスト変更OK、高速処理: extendメソッド 元リスト保持、複数リスト: unpack演算子 [*list1, *list2, *list3] 元リスト変更OK、通常処理: extend または +演算子

実践ガイドライン

覚えやすいガイドラインも用意しました。

# 実践的なガイドライン
print(f"=== 実践ガイドライン ===")
guidelines = [
"元のリストを変更したくない → +演算子",
"メモリ効率を重視 → extendメソッド",
"複数リストを一度に結合 → unpack演算子",
"可読性を重視 → +演算子またはunpack演算子",
"大量データの処理 → extendメソッド",
]
for guideline in guidelines:
print(f" • {guideline}")

実行結果:

=== 実践ガイドライン === • 元のリストを変更したくない → +演算子 • メモリ効率を重視 → extendメソッド • 複数リストを一度に結合 → unpack演算子 • 可読性を重視 → +演算子またはunpack演算子 • 大量データの処理 → extendメソッド

まとめ:リスト結合をマスターしよう

Pythonでリストを結合する方法について詳しく解説しました。 最後に重要なポイントをまとめておきますね。

2つの主要な方法

覚えておきたい基本の2つです:

+演算子

  • 新しいリストを作成
  • 元のリストは変更されない
  • 戻り値は新しいリスト
  • メソッドチェーンが可能

extendメソッド

  • 元のリストを直接変更
  • 新しいリストは作成されない
  • 戻り値はNone
  • パフォーマンスが良い

使い分けの基準

場面に応じて最適な方法を選びましょう:

元のリストを保持したい:+演算子 メモリ効率を重視したい:extendメソッド 複数リストを一度に結合:unpack演算子 大量データの処理:extendメソッド

実践での活用

リスト結合は基本的な操作ですが、適切な方法を選ぶことでコードの品質と性能が大きく改善されます。

パフォーマンスを重視する場面では、extendメソッドを選択しましょう。 元のデータを保持したい場面では、+演算子が安全です。

学習のポイント

段階的に習得していきましょう:

  1. 基本をマスター:+演算子とextendの違いを理解
  2. 実践で使い分け:状況に応じた選択
  3. 最適化:パフォーマンスを考慮した選択
  4. 応用:unpack演算子などの活用

リスト結合は、Pythonプログラミングの基本スキルです。 これらの方法を適切に使い分けることで、より効率的で読みやすいコードが書けるようになりますよ。

ぜひ実際のプログラミングで試してみてください! きっとコードの品質が向上するはずです。

関連記事