3.

Python リスト要素追加の全パターン(append / extend / insert / += / slice / deque 比較)

編集
この記事の要点
  • 末尾 1 要素 = list.append(x)。O(1) 償却で最速
  • 末尾に複数 = list.extend(iter) または +=append([1,2]) はネストするので別物
  • 任意位置に挿入 = list.insert(i, x)。先頭挿入は O(n) で遅い
  • スライス代入 a[i:j] = [...] は範囲をまるごと置換/挿入できる万能型
  • 先頭追加が頻繁なら collections.deque を使う。appendleft が O(1)

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 += bO(k)内部で extend を呼ぶ
a + bO(n+k)新リスト生成。元 a 不変
list.insert(i, x)O(n)i 以降をずらす
a[i:j] = [...]O(n)スライス置換
deque.append/appendleftO(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 が単一要素)TypeErrora + [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 か内包表記を使う。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. リストの作成
  2. 要素の参照
  3. リストへの要素追加(append / extend / insert / += / slice 代入)
  4. リスト要素の更新
  5. 要素の削除
  6. リスト要素数の取得

最近更新/作成されたページ