Pythonのリストは、スライス構文を使うことで、要素の一部を取り出したり、まとめて置き換えたり、逆順にしたりと、非常に柔軟な操作ができるデータ構造です。
本記事では、Pythonのリストスライスを「抽出」「更新」「応用」まで図解付きで丁寧に解説していきます。
初心者の方でも、最後まで読めば実務で困らないレベルの書き方が身につくことを目指します。
リストスライスとは
スライスの基本構文
まず最初に、リストスライスの基本構文を押さえます。
Pythonのリストスライスは、次のような形で書きます。
基本構文は次の3パターンです。
リスト[start:end]リスト[start:end:step]リスト[:]など、start・end・stepの一部を省略する形
ここで、start・end・stepにはそれぞれ次の意味があります。
start… 取り出しを開始する位置のインデックス(含まれる)end… 取り出しを終了する位置のインデックス(含まれない)step… 何個おきに要素を取るかを指定する間隔

Pythonのスライスでは、startは含まれ、endは含まれないというルールがあります。
このルールさえわかれば、範囲を迷わず指定できるようになります。
簡単なサンプルコードで動きを確認してみます。
# 基本となるリストを用意
nums = [10, 20, 30, 40, 50, 60]
# インデックス1以上3未満を取り出す
part = nums[1:3]
print("元のリスト:", nums)
print("スライス結果 nums[1:3]:", part)
元のリスト: [10, 20, 30, 40, 50, 60]
スライス結果 nums[1:3]: [20, 30]
このように、nums[1:3]は20(インデックス1)と30(インデックス2)が含まれ、40(インデックス3)は含まれません。
インデックスと範囲
リストスライスでは、インデックスとその範囲を意識して書くことが重要です。
まずはインデックスの考え方を整理しておきます。
Pythonのリストのインデックスは、左から0, 1, 2, …と番号が振られています。
たとえば次のようになります。

スライスにおける代表的なパターンを、インデックスの観点から説明します。
lst[a:b]… インデックスaからb-1までlst[:b]… 先頭(0)からb-1までlst[a:]… インデックスaから最後までlst[:]… 全体をコピー
次のサンプルで確認します。
data = ["a", "b", "c", "d", "e"]
print("data[1:4] :", data[1:4]) # インデックス1〜3
print("data[:3] :", data[:3]) # 先頭〜インデックス2
print("data[2:] :", data[2:]) # インデックス2〜最後
print("data[:] :", data[:]) # 全体
data[1:4] : ['b', 'c', 'd']
data[:3] : ['a', 'b', 'c']
data[2:] : ['c', 'd', 'e']
data[:] : ['a', 'b', 'c', 'd', 'e']
startまたはendを省略すると「先頭」「最後」までが自動的に補われるという振る舞いを、ここではしっかり押さえておきましょう。
負のインデックス(-1など)の意味
Pythonのリストでは、負のインデックスを使うことができ、-1は一番最後の要素を意味します。

負のインデックスの意味は次の通りです。
-1… 最後の要素-2… 後ろから2番目-3… 後ろから3番目
これをスライスで使うと、終端側を「後ろから何番目」と指定できるため、非常に便利です。
nums = [10, 20, 30, 40, 50, 60]
print("最後の要素 nums[-1] :", nums[-1])
print("後ろから2つ分 nums[-2:] :", nums[-2:])
print("先頭から最後の1つ手前まで nums[:-1] :", nums[:-1])
print("インデックス1〜後ろから2つ前まで nums[1:-2] :", nums[1:-2])
最後の要素 nums[-1] : 60
後ろから2つ分 nums[-2:] : [50, 60]
先頭から最後の1つ手前まで nums[:-1] : [10, 20, 30, 40, 50]
インデックス1〜後ろから2つ前まで nums[1:-2] : [20, 30, 40]
先頭側は正のインデックス、終端側は負のインデックスという組み合わせもよく使われるので、違和感なく読めるように慣れておくと良いです。
リストスライスでできる基本操作
部分リストの抽出
スライスのもっとも基本的な用途は、リストから一部の要素だけを切り出す「部分リストの抽出」です。

days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
# 平日のうち、火〜木を取り出す
mid_week = days[1:4]
print("元のリスト:", days)
print("部分リスト days[1:4]:", mid_week)
元のリスト: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
部分リスト days[1:4]: ['Tue', 'Wed', 'Thu']
ここで重要なのは、スライスの結果は新しいリストだという点です。
元のdaysは変更されません。
ステップ指定による間引き抽出
スライスにはstepという3つ目のパラメータがあり、間引きながら要素を取り出すことができます。
構文はリスト[start:end:step]です。
stepを2にすると「1つおき」、3にすると「2つ飛ばし」になります。

nums = list(range(10)) # [0, 1, 2, ..., 9]
print("元のリスト:", nums)
print("2つおき nums[::2]:", nums[::2]) # 0,2,4,6,8
print("3つおき nums[::3]:", nums[::3]) # 0,3,6,9
print("インデックス1〜8で2つおき nums[1:9:2]:", nums[1:9:2])
元のリスト: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2つおき nums[::2]: [0, 2, 4, 6, 8]
3つおき nums[::3]: [0, 3, 6, 9]
インデックス1〜8で2つおき nums[1:9:2]: [1, 3, 5, 7]
start・endを省略し、stepだけを指定する[::2]という書き方はよく使いますので、しっかり覚えておくと便利です。
逆順に並び替え
スライスのstepに-1を指定すると、リストを逆順にした新しいリストを取得できます。
これは、Pythonでよく見かける慣用表現です。

nums = [1, 2, 3, 4, 5]
rev = nums[::-1]
print("元のリスト:", nums)
print("逆順スライス nums[::-1]:", rev)
元のリスト: [1, 2, 3, 4, 5]
逆順スライス nums[::-1]: [5, 4, 3, 2, 1]
スライスによる逆順は「新しいリストを返す」点に注意してください。
元のリスト自体を逆順にしたいときは、nums.reverse()メソッドのほうが適切な場合がありますが、nums[::-1]は一時的に逆順にしたいときに便利です。
範囲外インデックスの扱いとエラーにならない理由
スライスを使うときに安心できるポイントとして、多少範囲外になってもエラーにならないという性質があります。
通常のインデックス参照では、次のように範囲外アクセスは例外エラーになります。
nums = [10, 20, 30]
# 通常のインデックス参照(エラー例)
# print(nums[5]) # IndexError: list index out of range
一方、スライスは多少オーバーしても安全です。
nums = [10, 20, 30]
print("nums[0:10]:", nums[0:10]) # 終了位置が範囲外
print("nums[-10:2]:", nums[-10:2]) # 開始位置が範囲外
print("nums[5:10]:", nums[5:10]) # 完全に範囲外
nums[0:10]: [10, 20, 30]
nums[-10:2]: [10, 20]
nums[5:10]: []
このように、スライスは「指定された範囲」と「実際のリストの範囲」の重なっている部分だけを返すように設計されています。
そのため、nums[5:10]のように全く重なりがない場合は空リスト[]が返り、エラーにはなりません。
リストスライスの代入と応用
スライス代入で要素をまとめて更新
スライスは、右辺として使うだけでなく、左辺に置いて代入することもできます。
これをスライス代入と呼びます。

nums = [10, 20, 30, 40, 50]
# インデックス1〜3(1以上4未満)をまとめて更新
nums[1:4] = [200, 300, 400]
print("スライス代入後:", nums)
スライス代入後: [10, 200, 300, 400, 50]
スライス代入の特徴として、代入するリストの長さが、元のスライス対象の長さと異なっていてもよいという点があります。
つまり、同時にリストの長さも変化し得るのです。
これを次のセクションで詳しく見ていきます。
要素の挿入と削除
スライス代入をうまく利用すると、要素の挿入と削除も直感的に行うことができます。
スライス代入で削除する
削除は、対象範囲に空リスト[]を代入することで実現できます。

nums = [1, 2, 3, 4, 5, 6]
# インデックス2〜4(2以上5未満)を削除
nums[2:5] = [] # 対象範囲を空リストで置き換える
print("削除後:", nums)
削除後: [1, 2, 6]
スライス範囲の要素がまるごと消えて、その後ろの要素が前に詰められる形になります。
スライス代入で挿入する
挿入では、長さ0のスライスに対してリストを代入します。
例えばnums[2:2]のように、startとendが同じ位置になる部分は、幅0のスライスです。
nums = [1, 2, 5, 6]
# インデックス2の位置に要素を挿入(幅0のスライスに代入)
nums[2:2] = [3, 4]
print("挿入後:", nums)
挿入後: [1, 2, 3, 4, 5, 6]
このように、スライス代入は「削除(短くする)」「挿入(長くする)」「置き換え」の3種類の操作を1つの仕組みで実現できるのが大きな特徴です。
ネストしたリストのスライス注意点
リストの中にリストが入っている、いわゆるネストしたリストに対してスライスを行う場合には、「どの階層のリストに対するスライスなのか」を意識する必要があります。

matrix = [
[1, 2, 3], # インデックス0
[4, 5, 6], # インデックス1
[7, 8, 9], # インデックス2
]
# 行方向にスライス(外側のリストに対するスライス)
top_rows = matrix[:2]
# 2行目の後ろ2つの要素をスライス(内側のリストに対するスライス)
row1_tail = matrix[1][1:]
print("top_rows:", top_rows)
print("row1_tail:", row1_tail)
top_rows: [[1, 2, 3], [4, 5, 6]]
row1_tail: [5, 6]
ここで注意したいのは、外側のリストをスライスコピーしても、内側のリストは同じものを参照しているという点です。
これは後述する「スライスとコピー」の話とも密接に関係します。
スライスとコピー
スライス[:]は「リスト全体の浅いコピー」を作るという使い方が非常に一般的です。

nums = [1, 2, 3]
copy1 = nums[:] # スライスでコピー
copy2 = list(nums) # list() コンストラクタでコピー
copy3 = nums.copy() # copy() メソッドでコピー
print("nums :", nums)
print("copy1 :", copy1)
print("copy2 :", copy2)
print("copy3 :", copy3)
# コピー後に元のリストを書き換えてみる
nums[0] = 99
print("書き換え後 nums :", nums)
print("書き換え後 copy1 :", copy1)
nums : [1, 2, 3]
copy1 : [1, 2, 3]
copy2 : [1, 2, 3]
copy3 : [1, 2, 3]
書き換え後 nums : [99, 2, 3]
書き換え後 copy1 : [1, 2, 3]
このように、スライス[:]で作ったコピーは、元のリストと独立して変更できます。
ただしこれは浅いコピー(シャローコピー)であり、ネストしたリストの内側まではコピーされないことに注意が必要です。
nested = [[1, 2], [3, 4]]
shallow = nested[:] # 浅いコピー
nested[0][0] = 99
print("nested :", nested)
print("shallow:", shallow)
nested : [[99, 2], [3, 4]]
shallow: [[99, 2], [3, 4]]
同じ内側リストを参照しているため、内側の要素を書き換えるとコピー側にも影響することがわかります。
ネストが深い構造を完全にコピーしたい場合は、copyモジュールのdeepcopyを使う必要があります。
リストスライスの書き方のコツ
よく使うスライス表現のパターン集
ここまでの内容の中で、実務でもよく登場するスライスの書き方を、代表的なパターンとして整理しておきます。
次の表は、よく使われるスライス表現とその意味をまとめたものです。
| スライス表現 | 意味・用途 |
|---|---|
lst[a:b] | インデックスa以上b未満の部分リストを取得 |
lst[:b] | 先頭からインデックスb-1までを取得 |
lst[a:] | インデックスaから最後までを取得 |
lst[:] | リスト全体の浅いコピー |
lst[::2] | 2つおき(偶数番目)に要素を取得 |
lst[1::2] | 奇数番目の要素を取得 |
lst[::-1] | 逆順に並べた新しいリストを取得 |
lst[-n:] | 後ろからn個の要素を取得 |
lst[:-n] | 最後のn個を除いた部分を取得 |
この表にあるパターンは、何度も目にして自然に読めるようになるまで使い込むことをおすすめします。
for文との組み合わせと注意点
スライスは、for文と組み合わせて「一部の要素だけをループ処理する」ときに便利です。

nums = list(range(10))
# インデックス2〜5だけを処理
for n in nums[2:6]:
print(n, end=" ")
2 3 4 5
ただし、スライスでコピーされたリストをfor文で回しても、元のリストは変更されない点には注意が必要です。
たとえば次のコードでは、元のnumsは書き換えられません。
nums = [1, 2, 3, 4, 5]
# nums[:] はコピーなので、ループで書き換えても元のnumsには影響しない
for i, n in enumerate(nums[:]):
nums[i] = n * 10
print("書き換え後 nums:", nums)
書き換え後 nums: [10, 20, 30, 40, 50]
この例ではenumerate(nums[:])でコピーをループしつつ、nums[i]を明示的に更新しているため、結果的に元のnumsも更新されています。
「どちらのリストを回していて」「どちらを書き換えているのか」を常に意識することが大切です。
スライスとリスト内包表記の使い分け
最後に、スライスとよく比較されるリスト内包表記との使い分けの考え方を整理します。

スライスは、「位置」や「インデックスのパターン」に基づいて要素をまとめて取り出すのに向いています。
一方、リスト内包表記は、「値の条件」や「変換処理」を伴うときに力を発揮します。
スライスが向いているケース
- 最初のn件だけ欲しいとき …
lst[:n] - 後ろからn件だけ欲しいとき …
lst[-n:] - 2つおき・3つおきに取りたいとき …
lst[::2]、lst[::3] - 逆順にしたいとき …
lst[::-1]
リスト内包表記が向いているケース
- 偶数だけ、負数だけなど、値の条件でフィルタしたいとき
- 各要素を2倍するなど、値を加工しながら新しいリストを作るとき
nums = list(range(10))
# スライス: インデックスの偶数番目(0,2,4,...)を選ぶ
by_index = nums[::2]
# 内包表記: 値が偶数のもの(0,2,4,...)を条件で選ぶ
by_value = [x for x in nums if x % 2 == 0]
print("スライス(インデックス基準):", by_index)
print("内包表記(値の条件基準):", by_value)
スライス(インデックス基準): [0, 2, 4, 6, 8]
内包表記(値の条件基準): [0, 2, 4, 6, 8]
この例ではたまたま結果が同じですが、考え方は「位置で選ぶ」か「条件で選ぶ」かという違いがあります。
状況に応じて、スライスと内包表記を使い分けられるようになると、コードがより読みやすく整理されます。
まとめ
Pythonのリストスライスは、シンプルな構文で「抽出」「整形」「更新」「コピー」までこなせる非常に強力な機能です。
start・end・stepの意味と、「startは含みendは含まない」というルール、負のインデックスの挙動、スライス代入による挿入・削除・置き換え、[:]による浅いコピーなどを理解しておけば、日常的なリスト操作のほとんどをスマートに書けるようになります。
本記事のサンプルとパターンを何度か手で動かしながら、ぜひ自分のコードでもスライス表現を積極的に活用してみてください。
