タイトル: リストへの要素追加(append / extend / insert / += / slice 代入)
SEOタイトル: Python リスト要素追加の全パターン(append / extend / insert / += / slice / deque 比較)
| この記事の要点 |
|
Python リストに要素を追加する 5 つの方法
Python のリストは「可変長配列」で、末尾追加が高速 (O(1) 償却) です。一方、先頭・中間挿入は配列を後ろにずらすため遅くなります。用途別に正しい API を選びます。
# ① 末尾に 1 個追加
a = [1, 2, 3]
a.append(4) # [1, 2, 3, 4]
# ② 末尾に複数追加(iterable を展開)
a.extend([5, 6]) # [1, 2, 3, 4, 5, 6]
a += [7, 8] # [1, 2, 3, 4, 5, 6, 7, 8] (extend と同じ)
# ③ 任意位置に挿入
a.insert(0, 0) # [0, 1, 2, 3, 4, 5, 6, 7, 8]
a.insert(len(a), 9) # append 相当
# ④ スライス代入(置換 / 挿入)
a[2:2] = [99, 98] # 位置 2 に挿入
a[2:4] = [] # 削除
# ⑤ 連結(新しいリストを作る)
b = a + [10, 11] # 元の a は変わらない
append と extend の違い
一番混乱する点。append は "1 要素" として追加、extend は "iterable を展開":
a = [1, 2, 3]
a.append([4, 5]) # → [1, 2, 3, [4, 5]] ← ネスト!
print(len(a)) # 4
b = [1, 2, 3]
b.extend([4, 5]) # → [1, 2, 3, 4, 5] ← 展開
print(len(b)) # 5
# extend は任意の iterable を受け付ける
c = [1, 2]
c.extend('ab') # → [1, 2, 'a', 'b']
c.extend(range(3)) # → [1, 2, 'a', 'b', 0, 1, 2]
c.extend({'x': 1, 'y': 2}) # キーが展開: [..., 'x', 'y']
各操作の計算量
| 操作 | 計算量 | 備考 |
|---|---|---|
list.append(x) | O(1) 償却 | 末尾。最速 |
list.extend(it) | O(k) k=追加数 | 末尾連結 |
a += b | O(k) | 内部で extend を呼ぶ |
a + b | O(n+k) | 新リスト生成。元 a 不変 |
list.insert(i, x) | O(n) | i 以降をずらす |
a[i:j] = [...] | O(n) | スライス置換 |
deque.append/appendleft | O(1) | 両端で O(1) |
スライス代入の柔軟さ
スライス代入は append/extend/insert/delete を全部表現できる「万能型」です:
a = [1, 2, 3, 4, 5]
# 末尾に extend と同じ
a[len(a):] = [6, 7] # [1,2,3,4,5,6,7]
# 先頭に挿入
a[:0] = [-1, 0] # [-1,0,1,2,3,4,5,6,7]
# 範囲を別リストで置換(長さ違いも OK)
a[2:4] = ['x', 'y', 'z'] # 2 要素を 3 要素に置換
# 範囲削除
a[2:4] = []
+= と + の違い(in-place vs 新オブジェクト)
a = [1, 2, 3]
id_before = id(a)
a += [4, 5] # in-place 変更(extend と同じ)
print(id(a) == id_before) # True
a = a + [6, 7] # 新リストを返す → a に再代入
print(id(a) == id_before) # False
# 関数引数で in-place 変更は破壊的
def add_item(lst):
lst += [99] # 呼び出し元の lst も変わる!
def add_item_safe(lst):
return lst + [99] # 新リスト返却、呼び出し元不変
先頭追加が頻繁なら deque を使う
list.insert(0, x) は O(n) なので、ループで使うと O(n^2) になります。両端アクセスが頻繁なら collections.deque:
from collections import deque
# list で先頭追加 → 遅い
a = []
for i in range(100000):
a.insert(0, i) # 毎回 O(n) → 全体 O(n^2)
# deque なら O(1)
d = deque()
for i in range(100000):
d.appendleft(i) # O(1)
d.append(i) # 末尾も O(1)
# deque ←→ list 変換は O(n)
lst = list(d)
よくある間違い
| 間違いコード | 症状 | 正しい書き方 |
|---|---|---|
a = a.append(x) | a が None になる | a.append(x) 代入しない |
a.append([1,2,3]) で展開したいのに | ネストになる | a.extend([1,2,3]) |
a + x(x が単一要素) | TypeError | a + [x] or a.append(x) |
大量データを insert(0, ...) | 非常に遅い | deque.appendleft |
関数内で lst += [x] | 呼び出し元も変わる | return lst + [x] |
パフォーマンス測定
import timeit
# append vs extend for 1 要素
print(timeit.timeit('a.append(1)', 'a=[]', number=1_000_000))
# 約 0.05s
# list.insert(0,...) vs deque.appendleft
print(timeit.timeit('a.insert(0, 1)', 'a=[]', number=10_000))
# 約 0.05s
print(timeit.timeit('a.appendleft(1)',
'from collections import deque; a=deque()', number=10_000))
# 約 0.001s ← 50 倍速い
FAQ
Q: append と += どちらが速い?
A: 1 要素追加なら append が僅かに速い。複数なら extend / +=。
Q: タプルや set にも append はある?
A: タプルは不変なので不可(a + (x,) で新タプル)。set は add(x)、複数なら update(iter)。
Q: ループ内連結で性能が落ちる
A: result = result + [x] は毎回新リスト生成で O(n^2)。append か内包表記を使う。