Pythonのリストから一部だけを取り出すには「スライス」が便利です。
範囲だけでなく、間隔や逆順も柔軟に指定できます。
この記事では基本構文からよく使う例、つまずきやすい注意点まで、初心者向けに順を追って丁寧に解説します。
リストのスライスとは
スライスとは、リストから一部分を切り出して新しいリストを作る機能のことです。
英語の「slice(薄切り)」のイメージ通り、元のリストを傷つけずに、必要な範囲だけを取り出せます。
位置(インデックス)の開始、終了、取り出す間隔をまとめて指定でき、データ前処理や抽出、並び替えの準備などに広く使われます。
スライスの基本構文と書き方
start stop stepの指定
スライスの基本形は、list[start:stop:step]
です。
start
は開始位置、stop
は終了位置(ただしstop
の位置は含みません)、step
は何個おきに取り出すかの間隔です。
たとえば2つおきに取り出したい、というような指定ができます。
stepは省略可能ですが、0は指定できません。
# 基本のサンプル
nums = [10, 20, 30, 40, 50, 60, 70]
# start=1, stop=5, step=2 → インデックス1(20)から1つ飛ばしでstop直前(インデックス4まで)
part1 = nums[1:5:2] # [20, 40]
# start=0, stop=3(=インデックス0〜2) → 最初の3つ
part2 = nums[0:3] # [10, 20, 30]
# stepに0は指定不可 → 例外(ValueError)になるので捕捉して表示
try:
bad = nums[::0]
except ValueError as e:
print("エラー:", e)
print("part1:", part1)
print("part2:", part2)
エラー: slice step cannot be zero
part1: [20, 40]
part2: [10, 20, 30]
stopは含まない
スライスのstopは「含まない(排他的)」のが重要なポイントです。
開始位置は含む一方、終了位置は含まれません。
したがって、意図した数に満たない結果になっていないか、最初は出力を確認しながら慣れると安心です。
nums = [10, 20, 30, 40, 50, 60]
# インデックスと値の対応を確認しやすいように表示
print("インデックスと値:", list(enumerate(nums)))
# インデックス1から3まで(3は含まないので実際は1,2が対象)
print("nums[1:3] →", nums[1:3]) # [20, 30]
# インデックス1から4まで(4は含まないので1,2,3が対象)
print("nums[1:4] →", nums[1:4]) # [20, 30, 40]
インデックスと値: [(0, 10), (1, 20), (2, 30), (3, 40), (4, 50), (5, 60)]
nums[1:3] → [20, 30]
nums[1:4] → [20, 30, 40]
省略時の動き
start
、stop
、step
は省略できます。
省略したときのデフォルトは以下の通りです。
省略項目 | デフォルト値 | 意味 |
---|---|---|
start | 0 | 先頭から開始 |
stop | len(list) | 末尾の次(末尾まで) |
step | 1 | 1つずつ取り出す |
nums = [10, 20, 30, 40, 50, 60]
# 先頭からインデックス3の直前まで
print("nums[:3] →", nums[:3]) # [10, 20, 30]
# インデックス3から末尾まで
print("nums[3:] →", nums[3:]) # [40, 50, 60]
# 2つおきに取り出す
print("nums[::2] →", nums[::2]) # [10, 30, 50]
# すべて(完全コピーと同じ結果。ただしこの書き方の解説は後述)
print("nums[:] →", nums[:]) # [10, 20, 30, 40, 50, 60]
nums[:3] → [10, 20, 30]
nums[3:] → [40, 50, 60]
nums[::2] → [10, 30, 50]
nums[:] → [10, 20, 30, 40, 50, 60]
スライスとインデックスの違い
インデックス(list[i])は単一要素を取り出し、スライス(list[i:j])はリストを返します。
この違いは型やエラーの出方にも影響します。
nums = [10, 20, 30, 40, 50]
# 単一要素の取り出し(型はint)
one = nums[2]
print("nums[2]:", one, "型:", type(one))
# 1要素でもスライスはリスト(型はlist)
sub = nums[2:3]
print("nums[2:3]:", sub, "型:", type(sub))
# インデックスは範囲外だと例外(IndexError)
try:
_ = nums[100]
except IndexError as e:
print("インデックスの範囲外:", e)
# スライスは範囲外でも空リストが返る
print("nums[100:101]:", nums[100:101])
nums[2]: 30 型: <class 'int'>
nums[2:3]: [30] 型: <class 'list'>
インデックスの範囲外: list index out of range
nums[100:101]: []
よく使うスライス例
先頭から指定位置まで
先頭からn個分を取り出すときは [:n] を使います。
nは「含まない位置」なので、n=3ならインデックス0〜2が対象です。
letters = ["a", "b", "c", "d", "e", "f"]
head3 = letters[:3]
print(head3) # ["a", "b", "c"]
['a', 'b', 'c']
指定位置から末尾まで
ある位置以降をすべて取り出すには [n:] を使います。
nは開始位置(含む)です。
letters = ["a", "b", "c", "d", "e", "f"]
tail_from_3 = letters[3:] # インデックス3(= "d")から末尾まで
print(tail_from_3)
['d', 'e', 'f']
指定範囲の部分リスト
i以上、j未満の範囲を取り出すには [i:j] を使います。
範囲の両端を明確に決めたいときに便利です。
letters = ["a", "b", "c", "d", "e", "f"]
mid = letters[2:5] # インデックス2〜4 ("c","d","e")
print(mid)
['c', 'd', 'e']
逆順のリスト
全体を逆順にしたいときは [::-1] が最短です。
部分的に逆方向へ進めたいときは開始と終了を逆転させ、stepに-1を指定します。
letters = ["a", "b", "c", "d", "e", "f"]
all_rev = letters[::-1] # 全体の逆順
part_rev = letters[4:1:-1] # インデックス4から2まで逆方向("e","d","c")
print("全体逆順:", all_rev)
print("部分逆順:", part_rev)
全体逆順: ['f', 'e', 'd', 'c', 'b', 'a']
部分逆順: ['e', 'd', 'c']
一定間隔で取り出す
等間隔で要素を抽出するには step を使います。
偶数番目(0始まり)や奇数番目だけを取り出す用途にもよく使います。
letters = ["a", "b", "c", "d", "e", "f", "g"]
even_indexed = letters[::2] # 0,2,4,6番目
odd_indexed = letters[1::2] # 1,3,5番目
every_three = letters[::3] # 3つおき
print("2つおき:", even_indexed)
print("1つずらしで2つおき:", odd_indexed)
print("3つおき:", every_three)
2つおき: ['a', 'c', 'e', 'g']
1つずらしで2つおき: ['b', 'd', 'f']
3つおき: ['a', 'd', 'g']
スライスのコツと注意点
負のインデックスと負のステップ
負のインデックスは「末尾からの数え方」を表します。
-1は最後、-2は最後から2番目を意味します。
また、stepを負にすると右から左へ進みます。
s = ["a", "b", "c", "d", "e", "f"]
print("s[-1] (最後):", s[-1]) # 'f'
print("s[-3:-1] (最後から3〜1手前):", s[-3:-1]) # ['d', 'e']
print("s[::-1] (全体逆順):", s[::-1]) # ['f','e','d','c','b','a']
# 最後から3つ取り出す(逆方向で区間を指定)
print("s[-1:-4:-1]:", s[-1:-4:-1]) # ['f','e','d']
s[-1] (最後): f
s[-3:-1] (最後から3〜1手前): ['d', 'e']
s[::-1] (全体逆順): ['f', 'e', 'd', 'c', 'b', 'a']
s[-1:-4:-1]: ['f', 'e', 'd']
範囲外でもエラーにならない
インデックスと違い、スライスは範囲外でも例外にならず、可能な部分を返すか空リストになります。
これにより安全に切り出しができます。
s = ["a", "b", "c", "d", "e"]
# インデックスは範囲外で例外
try:
_ = s[100]
except IndexError as e:
print("インデックス範囲外:", e)
# スライスは範囲外でも安全
print("s[100:200]:", s[100:200]) # 空リスト
print("s[-100:3]:", s[-100:3]) # 先頭〜インデックス2
print("s[3:100]:", s[3:100]) # インデックス3〜末尾まで
インデックス範囲外: list index out of range
s[100:200]: []
s[-100:3]: ['a', 'b', 'c']
s[3:100]: ['d', 'e']
スライスが返すコピー
スライスは新しいリスト(コピー)を返します。
したがって、取り出した側を変更しても元のリストは変わりません。
ただし「浅いコピー(シャローコピー)」なので、ネストしたリストの中身は共有される点に注意が必要です。
# 単純なリストでは独立している
a = [1, 2, 3, 4]
b = a[1:3] # [2,3] の新しいリスト
b[0] = 999
print("a:", a) # 変更されない
print("b:", b)
# ネストしたリストでは中身(内側のリスト)は共有される
nested = [[1, 2], [3, 4]]
shallow = nested[:] # 浅いコピー
shallow[0][0] = 99 # 内側の要素を書き換える
print("nested:", nested) # 影響を受ける
print("shallow:", shallow)
a: [1, 2, 3, 4]
b: [999, 3]
nested: [[99, 2], [3, 4]]
shallow: [[99, 2], [3, 4]]
内側のオブジェクトも独立させたい場合は、copy.deepcopyを検討します。
import copy
nested = [[1, 2], [3, 4]]
deep = copy.deepcopy(nested) # 深いコピー
deep[0][0] = 99
print("nested:", nested) # 影響なし
print("deep:", deep)
nested: [[1, 2], [3, 4]]
deep: [[99, 2], [3, 4]]
リスト全体をコピーする
リスト全体のコピーにはいくつか方法があります。
[:]、list()、copy()はいずれも新しいリストを作る点は共通ですが、代入(=)は単なる参照の共有になります。
方法 | 新しいリストか | 備考 |
---|---|---|
a[:] | はい | スライスで全体コピー(浅いコピー) |
list(a) | はい | コンストラクタでコピー(浅いコピー) |
a.copy() | はい | メソッドでコピー(浅いコピー) |
b = a | いいえ | 参照を共有(同じオブジェクト) |
a = [1, 2, 3]
b = a[:] # スライス
c = list(a) # コンストラクタ
d = a.copy() # メソッド
e = a # 参照の共有(エイリアス)
print("a is b:", a is b)
print("a is c:", a is c)
print("a is d:", a is d)
print("a is e:", a is e) # これだけTrue
# 参照共有の違いを確認
e[0] = 999
print("a:", a) # aも変わる
print("e:", e)
print("b:", b) # b, c, dは変わらない
print("c:", c)
print("d:", d)
a is b: False
a is c: False
a is d: False
a is e: True
a: [999, 2, 3]
e: [999, 2, 3]
b: [1, 2, 3]
c: [1, 2, 3]
d: [1, 2, 3]
まとめ
スライスは、list[start:stop:step]
という簡潔な書き方で、範囲指定、逆順、間隔抽出などを柔軟にこなせる強力な機能です。
特に「stopは含まない」「省略時のデフォルト(start=0
、stop=len(list)
、step=1
)」「範囲外でもエラーにならない」という3点を押さえると、思い通りに操作しやすくなります。
また、スライスは新しいリストを返すため安全に加工できますが、浅いコピーであることや、stepに0は指定できないことには注意してください。
基本とコツを身につければ、データの前処理や部分抽出が格段に書きやすくなります。