スライスの構文と基本動作
Python のリストや文字列などのシーケンス型から、連続した要素を取り出すにはスライスを使います。書式は [start:stop:step] の形で、start を省略すると先頭、stop を省略すると末尾、step を省略すると 1 ずつ進みます。
data = [10, 20, 30, 40, 50, 60, 70, 80, 90]
# 末尾を超えた指定でもエラーにならない
X = 100
data[0:X] == data[0:] == data[:X] == data[:] == data[::]
# 正のステップ
data[1:5] # [20, 30, 40, 50]
data[1:5:2] # [20, 40]
data[-1:] # [90]
data[-4:-2] # [60, 70]
data[:-2] # [10, 20, 30, 40, 50, 60, 70]
# 負のステップ(逆順に走査)
data[::-1] # [90, 80, 70, 60, 50, 40, 30, 20, 10]
data[::-2] # [90, 70, 50, 30, 10]
data[:-5:-1] # [90, 80, 70, 60]
data[:-5:-3] # [90, 60]
# ステップ 0 は禁止
data[::0] # ValueError: slice step cannot be zero
負のステップを使う場合は「リストを逆転させてから範囲を取り出す」とイメージすると分かりやすいです。また、start や stop に長さを超える値を指定しても IndexError にはなりません。
スライス結果は新しいオブジェクト、ただし浅いコピー
スライスは元のシーケンスとは別の新しいオブジェクトを返します。ただし、その中身は浅いコピー(shallow copy)なので、可変オブジェクトが要素に含まれている場合は元のリストと影響し合います。
src = [1, [10, 20], 3, 4]
dst = src[:]
src == dst # True
id(src) == id(dst) # False
# 内側の可変オブジェクトは同じ参照
src[1].append(99)
print(dst[1]) # [10, 20, 99]
# スライスは独立した値として使える
src.append(src[2:4]) # 末尾に [3, 4] が追加される
dst.extend(dst[2:4]) # 3, 4 が展開されて追加される
# 特定長以上かどうかの判定にも利用できる
if data[8:]:
print("要素が9個以上")
スライスを「穴」として使う:挿入、置換、削除
スライスは値を取り出すだけでなく、代入先として使うこともできます。範囲が空のスライスに代入すると、特定位置に要素を挿入できます。このとき、右辺は必ず iterable である必要があります。
items = [1, 2, 3, 4]
# 先頭へ挿入
items[:0] = [0] # [0, 1, 2, 3, 4]
# 末尾へ挿入
items[len(items):] = [5, 7] # [0, 1, 2, 3, 4, 5, 7]
# 中間へ挿入
items[5:5] = [6] # [0, 1, 2, 3, 4, 5, 6, 7]
# 非 iterable は代入不可
items[2:2] = 99 # TypeError
items[:0] = (9,) # OK
items[:0] = range(3) # OK
空でないスライスに代入すると、指定区間を置換します。置換元と置換先の長さは一致させる必要がありません。
vals = [1, 2, 3, 4]
vals[:2] = [7, 8, 9] # [7, 8, 9, 3, 4]
vals[2:4] = ['a', 'b'] # [7, 8, 'a', 'b']
vals[1:3] = [1, 2, 3, 4] # [7, 1, 2, 3, 4, 'b']
# 削除
del vals[2:4] # [7, 1, 'b']
ステップを指定したスライスへの代入は、等長でない場合はエラーになります。
nums = [1, 2, 3, 4, 5, 6]
nums[::2] = ['a', 'b', 'c'] # ['a', 2, 'b', 4, 'c', 6]
nums[::2] = ['w'] # ValueError: 等長でないため不可
del nums[::2] # [2, 4, 6]
他言語における「スライス」の位置づけ
Python のスライスはシーケンスから部分列を取り出すための構文です。一方、Go 言語には slice という独立した型が存在し、配列の一部を参照します。Go のスライスは長さ(len)と容量(cap)を持ち、必要に応じて動的に領域を拡張します。対して Python のスライスはあくまで元のシーケンスを操作するための構文であり、新しいシーケンスを返すか、代入先として動作します。