Pythonのスライスとは、シーケンス[start:stop:step] という書き方で、リスト・文字列・タプルなどから「連続した一部分」を新しいオブジェクトとして取り出す機能です。 start は開始位置、stop は終了位置(その位置自体は含まない)、step は何個おきに取るかを表し、3つとも省略できます。

この記事では、先頭n件・末尾n件・逆順といった「やりたいこと」から書き方を引ける早見表を最初に置き、そのあとで仕組みを順に解説します。掲載しているコードと出力は、すべて Python 3.14.7 で実際に動かした結果です(スライスの基本的な挙動は Python 3 系で共通です)。範囲外を指定してもエラーにならない理由、逆順にしたときに結果が空になる原因、スライス代入・del・浅いコピー・slice() オブジェクトまで、つまずきやすい箇所はエラー文つきで確認していきます。

Pythonスライスの早見表

次の3つのデータで、同じ書き方がどう動くかを並べています。

a = [0, 1, 2, 3, 4, 5]   # リスト
s = "abcdef"             # 文字列
t = (0, 1, 2, 3, 4, 5)   # タプル
やりたいこと 書き方 リスト a 文字列 s タプル t
先頭3件 x[:3] [0, 1, 2] 'abc' (0, 1, 2)
末尾3件 x[-3:] [3, 4, 5] 'def' (3, 4, 5)
2番目以降(先頭2件を除く) x[2:] [2, 3, 4, 5] 'cdef' (2, 3, 4, 5)
末尾2件を除く x[:-2] [0, 1, 2, 3] 'abcd' (0, 1, 2, 3)
1番目から3番目まで x[1:4] [1, 2, 3] 'bcd' (1, 2, 3)
先頭と末尾を1件ずつ除く x[1:-1] [1, 2, 3, 4] 'bcde' (1, 2, 3, 4)
1つおき(偶数番目) x[::2] [0, 2, 4] 'ace' (0, 2, 4)
1つおき(奇数番目) x[1::2] [1, 3, 5] 'bdf' (1, 3, 5)
3つおき x[::3] [0, 3] 'ad' (0, 3)
逆順 x[::-1] [5, 4, 3, 2, 1, 0] 'fedcba' (5, 4, 3, 2, 1, 0)
逆順で1つおき x[::-2] [5, 3, 1] 'fdb' (5, 3, 1)
末尾3件を逆順で x[:-4:-1] [5, 4, 3] 'fed' (5, 4, 3)
先頭3件を逆順で x[2::-1] [2, 1, 0] 'cba' (2, 1, 0)
全体のコピー x[:] [0, 1, 2, 3, 4, 5] 'abcdef' (0, 1, 2, 3, 4, 5)
範囲外を指定 x[4:100] [4, 5] 'ef' (4, 5)
範囲がすべて外 x[10:20] [] '' ()

スライスの結果は、元と同じ型で返ります。 リストからはリスト、文字列からは文字列、タプルからはタプルです。番号は0から数える点と、stop の位置は含まれない点だけ先に押さえておくと、表の残りは同じ規則で読めます。

リストだけで使える「書き換え系」の操作は次のとおりです(文字列とタプルは変更できない型なので使えません)。

やりたいこと 書き方 実行後の a(元は [0, 1, 2, 3, 4, 5])
一部を置き換える a[1:3] = [10, 20] [0, 10, 20, 3, 4, 5]
個数を変えて置き換える a[1:3] = [9] [0, 9, 3, 4, 5]
途中に挿入する a[2:2] = [7, 8] [0, 1, 7, 8, 2, 3, 4, 5]
一部を削除する del a[1:3] [0, 3, 4, 5]
1つおきに削除する del a[::2] [1, 3, 5]
中身を空にする del a[:] []

(この表の結果は、後半の「スライス代入」「del」の章で1行ずつ実行して確かめています。)

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Pythonスライスの使い方早見表|リスト・文字列の切り出しと逆順」

スライスの基本ルール:start・stop・step

書き方は3通り

書き方 意味 省略したときの値
x[start:stop] start から stop の手前まで start は先頭、stop は末尾まで
x[start:stop:step] step 個おきに取る step は 1
x[::step] 全体を step 個おきに取る step が負なら末尾から先頭へ

覚えるポイントは「stop は含まない」の1点です。 x[1:4] は 1・2・3 番目を取り、4 番目は取りません。そのおかげで、x[:n] と x[n:] をつなげるとちょうど元に戻り、取り出される個数は stop - start で計算できます。

a = [0, 1, 2, 3, 4, 5]

print(a[1:4])            # 1番目から4番目の手前まで
print(len(a[1:4]))       # 個数は 4 - 1 = 3
print(a[:2] + a[2:])     # 前半と後半をつなぐと元どおり
print(a[:2] + a[2:] == a)

実行結果:

[1, 2, 3]
3
[0, 1, 2, 3, 4, 5]
True

位置は「要素のすき間」に番号があると考える

インデックスを要素そのものではなく、要素と要素のあいだの「切れ目」に付いた番号として見ると、stop を含まない理由が自然に理解できます。

文字列 "abcdef" の切れ目

 切れ目   0   1   2   3   4   5   6
          | a | b | c | d | e | f |
 負の数  -6  -5  -4  -3  -2  -1

s[1:4] は「切れ目1から切れ目4まで」なので bcd です。負の数は末尾から数えた位置で、-1 は最後の要素の手前の切れ目を指します。そのため s[-3:] は「後ろから3つ目の切れ目から最後まで」で def になります。

指定 読み方 結果
s[1:4] 切れ目1〜4 'bcd'
s[-3:] 後ろから3つ目〜最後 'def'
s[:-3] 最初〜後ろから3つ目 'abc'
s[-5:-2] 後ろから5つ目〜後ろから2つ目 'bcd'
s[2:-1] 正と負を混ぜてもよい 'cde'

インデックス指定とスライスの違い

x[2] のように数字1つで取り出すのは「インデックス指定」、コロンを使うのが「スライス」です。見た目は似ていますが、返ってくるものが違います。

比べる点 インデックス a[2] スライス a[2:3]
返るもの 要素そのもの(2) 要素1つのリスト([2])
範囲外を指定 IndexError が発生 空のリストが返る
向いている用途 1件だけ確実に取り出す 件数が決まっていない一部分を取り出す

やりたいこと別の書き方(コピペ用)

ここからは、よく使う切り出しを1つずつコードで確認します。数値のリストだけだと位置と値の区別がつきにくいので、タスク名のリストを例にします。

先頭n件・末尾n件を取り出す

tasks = ["設計", "実装", "レビュー", "テスト", "リリース", "振り返り"]
n = 2

print(tasks[:n])     # 先頭n件
print(tasks[-n:])    # 末尾n件
print(tasks[n:])     # 先頭n件を除いた残り
print(tasks[:-n])    # 末尾n件を除いた残り

実行結果:

['設計', '実装']
['リリース', '振り返り']
['レビュー', 'テスト', 'リリース', '振り返り']
['設計', '実装', 'レビュー', 'テスト']

「先頭n件」は [:n]、「末尾n件」は [-n:] と覚えておけば、ほとんどの場面で足ります。ただし末尾n件は n が 0 のときだけ注意が必要で、これは後の「範囲外でもエラーにならない」の章で扱います。

n番目からm番目までを取り出す

tasks = ["設計", "実装", "レビュー", "テスト", "リリース", "振り返り"]

print(tasks[1:4])        # インデックス1〜3(4は含まない)
start, count = 2, 3
print(tasks[start:start + count])   # 2番目から3件

実行結果:

['実装', 'レビュー', 'テスト']
['レビュー', 'テスト', 'リリース']

「何番目から何件」という取り方は、x[start:start + count] と書くと意図がそのまま伝わります。一覧をページごとに表示する処理でもよく使う形です。

items = ["A", "B", "C", "D", "E", "F", "G"]
per_page = 3

for page in range(1, 4):
    start = (page - 1) * per_page
    print(page, items[start:start + per_page])

実行結果:

1 ['A', 'B', 'C']
2 ['D', 'E', 'F']
3 ['G']

最後のページは件数が足りなくても、そのまま残りだけが返ります。件数を自分で数えて調整する必要はありません。

1つおき・n個おきに取り出す

nums = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

print(nums[::2])     # 0, 2, 4, ... 番目
print(nums[1::2])    # 1, 3, 5, ... 番目
print(nums[::3])     # 3つおき
print(nums[2:8:2])   # 2番目から8番目の手前までを1つおき

実行結果:

[10, 12, 14, 16, 18]
[11, 13, 15, 17, 19]
[10, 13, 16, 19]
[12, 14, 16]

step を指定すると、start の位置から step ずつ進んだ要素が取り出されます。偶数番目と奇数番目に分けたいときは [::2] と [1::2] の組み合わせが便利です。

逆順にする

nums = [1, 2, 3, 4, 5]
word = "Python"

print(nums[::-1])    # リストを逆順に
print(word[::-1])    # 文字列を逆順に
print(nums)          # 元のリストは変わらない

実行結果:

[5, 4, 3, 2, 1]
nohtyP
[1, 2, 3, 4, 5]

[::-1] は元のデータを変えずに、逆順の新しいオブジェクトを作ります。 逆順にする方法はほかにもあり、目的によって選べます。

方法 返るもの 元のデータ 使える型
x[::-1] 逆順の新しいオブジェクト(元と同じ型) 変わらない リスト・文字列・タプルなど
reversed(x) 逆順にたどるイテレータ 変わらない 同上(for文で回すときに向く)
x.reverse() None(その場で並べ替える) 逆順に変わる リストのみ
nums = [1, 2, 3, 4, 5]

print(list(reversed(nums)))   # イテレータなので list() で中身を見る
print("".join(reversed("Python")))

nums.reverse()                # その場で逆順にする(戻り値は None)
print(nums)

実行結果:

[5, 4, 3, 2, 1]
nohtyP
[5, 4, 3, 2, 1]

文字列を逆順にした結果を文字列のまま得たいときは、[::-1] が一番短く書けます。

範囲外でもIndexErrorにならない

インデックス指定とスライスで挙動が違う

インデックス指定は範囲外で IndexError になりますが、スライスは範囲外を指定しても例外になりません。 はみ出した部分は自動的に端に丸められます。

a = [0, 1, 2, 3, 4, 5]

print(a[4:100])     # stop が大きすぎる → 末尾まで
print(a[-100:2])    # start が小さすぎる → 先頭から
print(a[10:20])     # 範囲がすべて外 → 空のリスト
print(a[3:1])       # start が stop より後ろ → 空のリスト

try:
    print(a[10])    # インデックス指定は例外になる
except IndexError as e:
    print("IndexError:", e)

実行結果:

[4, 5]
[0, 1]
[]
[]
IndexError: list index out of range

今回、公式ドキュメント(Python 3.14 の「組み込み型」→「共通のシーケンス演算」)を開いて確認したところ、s[i:j] について次の規則が書かれていました。

条件 扱い
i を省略、または None 0 として扱う
j を省略、または None len(s) として扱う
i または j が -len(s) より小さい 0 として扱う
i または j が len(s) より大きい len(s) として扱う
i が j 以上 空のスライスになる

この性質は、「最大10件まで表示する」のような処理を if 文なしで書けるという利点になります。

logs = ["起動", "接続", "完了"]

print(logs[:10])        # 3件しかなくても、そのまま3件返る
print(logs[:10] == logs)

empty = []
print(empty[:1])        # 空のリストでも例外にならない
print(empty[0:1] or ["(なし)"])   # 先頭1件、なければ既定値

実行結果:

['起動', '接続', '完了']
True
[]
['(なし)']

「空のリストが返る」ことに気づきにくい場面

例外にならないということは、位置の指定を間違えても静かに空の結果が返るということでもあります。結果が空になったときは、次の表を上から確認すると原因を絞りやすくなります。

症状 原因 直し方
a[3:1] が空 start が stop より後ろ 順番を入れ替えて a[1:3]
a[1:4:-1] が空 step が負なのに start が stop より前 a[3:0:-1] のように start を後ろにする
a[len(a):] が空 start が末尾ちょうど 意図どおりなら問題なし
a[-0:] が全件 -0 は 0 と同じ 次の項の書き方にする

末尾n件でnが0のときの落とし穴

x[-n:] は n が 0 のときだけ結果が変わります。-0 は 0 と同じなので、x[-0:] は x[0:]、つまり全件になります。

a = [0, 1, 2, 3, 4, 5]

for n in (2, 1, 0):
    print(n, a[-n:])                 # n=0 のとき全件になる

print("--- 直したあと ---")
for n in (2, 1, 0):
    print(n, a[len(a) - n:])         # n=0 のとき空になる

実行結果:

2 [4, 5]
1 [5]
0 [0, 1, 2, 3, 4, 5]
--- 直したあと ---
2 [4, 5]
1 [5]
0 []

n が変数で 0 になり得るときは、x[len(x) - n:] と書くと、0件のときに空が返ります。n がリストの長さを超える可能性があるなら、x[max(len(x) - n, 0):] とすると安心です。

逆順(負のstep)でのstart・stopの落とし穴

step を負にすると、進む向きが「後ろから前」に変わります。このとき start は後ろ側、stop は前側に指定します。向きが逆だと、エラーにはならず空の結果になります。

a = [0, 1, 2, 3, 4, 5]

print(a[1:4:-1])     # 空になる(1から後ろ向きに進んでも4には届かない)
print(a[4:1:-1])     # 4番目から2番目まで(1は含まない)
print(a[4::-1])      # 4番目から先頭まで
print(a[:2:-1])      # 末尾から3番目まで(2は含まない)

実行結果:

[]
[4, 3, 2]
[4, 3, 2, 1, 0]
[5, 4, 3]

先頭まで含めたいときに -1 と書くと空になる

「4番目から0番目までを逆順で」と考えて stop に -1 を書くと、-1 は「末尾の要素」を意味するため、結果は空になります。

a = [0, 1, 2, 3, 4, 5]

print(a[4:0:-1])      # 0番目が含まれない
print(a[4:-1:-1])     # -1 は末尾の位置なので空
print(a[4::-1])       # stop を省略すると先頭まで
print(a[4:None:-1])   # None でも同じ

実行結果:

[4, 3, 2, 1]
[]
[4, 3, 2, 1, 0]
[4, 3, 2, 1, 0]

逆順で先頭まで取りたいときは、stop を省略するか None を指定します。 stop を変数で渡す関数を書くときは、None を使うと分岐が要りません。

省略時の値は step の符号で変わる

step start を省略 stop を省略
正(例:1) 先頭から 末尾まで
負(例:-1) 末尾から 先頭まで(先頭を含む)

公式ドキュメントにも、省略または None のときは「端」の値が使われ、どちらの端かは step の符号で決まると書かれています。「範囲を切ってから逆順」と「逆順にしてから範囲を切る」は結果が違うので、迷ったら2段階に分けて書くと読みやすくなります。

a = [0, 1, 2, 3, 4, 5]

# 「1〜3番目を取り出して、逆順にする」を2通りで書く
print(a[1:4][::-1])   # 2段階:読みやすい
print(a[3:0:-1])      # 1回で書く:start と stop を計算する必要がある

実行結果:

[3, 2, 1]
[3, 2, 1]

step に 0 は指定できない

a = [0, 1, 2, 3, 4, 5]

try:
    print(a[::0])
except ValueError as e:
    print("ValueError:", e)

実行結果:

ValueError: slice step cannot be zero

スライス代入でリストを書き換える

リストは変更できる型なので、スライスを代入文の左辺に置くと、その範囲をまるごと別の要素に入れ替えられます。

置き換え・挿入・削除を1つの書き方で行う

a = [0, 1, 2, 3, 4, 5]
a[1:3] = [10, 20]          # 同じ個数で置き換え
print(a)

a = [0, 1, 2, 3, 4, 5]
a[1:3] = [9]               # 2個を1個に(リストが短くなる)
print(a)

a = [0, 1, 2, 3, 4, 5]
a[1:3] = [7, 8, 9, 10]     # 2個を4個に(リストが長くなる)
print(a)

a = [0, 1, 2, 3, 4, 5]
a[2:2] = [7, 8]            # 幅0の範囲に代入 → 挿入
print(a)

a = [0, 1, 2, 3, 4, 5]
a[1:3] = []                # 空のリストを代入 → 削除
print(a)

a = [0, 1, 2, 3, 4, 5]
a[len(a):] = [6, 7]        # 末尾に追加(extend と同じ結果)
print(a)

実行結果:

[0, 10, 20, 3, 4, 5]
[0, 9, 3, 4, 5]
[0, 7, 8, 9, 10, 3, 4, 5]
[0, 1, 7, 8, 2, 3, 4, 5]
[0, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6, 7]

step を使わないスライス代入では、右辺の個数は左辺と違っていても構いません。 リストの長さがそれに合わせて変わります。

右辺はイテラブルなら何でもよい

右辺には「順に取り出せるもの」を置きます。文字列を置くと1文字ずつに分かれる点は、知っておくと驚かずに済みます。

a = [0, 1, 2, 3, 4, 5]
a[1:3] = "xy"              # 文字列は1文字ずつに分かれる
print(a)

a = [0, 1, 2, 3, 4, 5]
a[1:3] = ["xy"]            # 1要素として入れたいならリストで包む
print(a)

a = [0, 1, 2, 3, 4, 5]
try:
    a[1:3] = 99            # イテラブルでない値は代入できない
except TypeError as e:
    print("TypeError:", e)

実行結果:

[0, 'x', 'y', 3, 4, 5]
[0, 'xy', 3, 4, 5]
TypeError: must assign iterable to extended slice

stepつきのスライス代入は個数をそろえる

step を 1 以外にした場合は、右辺の個数が左辺の個数と一致している必要があります。

a = [0, 1, 2, 3, 4, 5]
a[::2] = ["a", "b", "c"]   # 3か所を3個で置き換え
print(a)

try:
    a[::2] = ["x", "y"]    # 3か所に2個 → ValueError
except ValueError as e:
    print("ValueError:", e)

実行結果:

['a', 1, 'b', 3, 'c', 5]
ValueError: attempt to assign sequence of size 2 to extended slice of size 3

a = と a[:] = の違い

a = [...] は変数 a が別のリストを指すようにするだけですが、a[:] = [...] は同じリストの中身を入れ替えます。 同じリストを別の変数や関数の引数でも参照しているときに違いが出ます。

a = [1, 2, 3]
b = a                # b は a と同じリストを指す

a = [9, 9]           # a だけが新しいリストを指す
print(a, b)

a = [1, 2, 3]
b = a
a[:] = [9, 9]        # 同じリストの中身を入れ替える
print(a, b)

実行結果:

[9, 9] [1, 2, 3]
[9, 9] [9, 9]

文字列とタプルには代入できない

文字列とタプルは変更できない型(イミュータブル)なので、スライス代入や del は TypeError になります。新しい値を組み立てて、変数に入れ直すのが定番の書き方です。

s = "abcdef"
try:
    s[1:3] = "XY"
except TypeError as e:
    print("TypeError:", e)

s = s[:1] + "XY" + s[3:]      # 前後をスライスで取り出してつなぐ
print(s)

t = (0, 1, 2, 3)
try:
    del t[1:3]
except TypeError as e:
    print("TypeError:", e)

t = t[:1] + t[3:]             # タプルも同じ考え方
print(t)

実行結果:

TypeError: 'str' object does not support item assignment
aXYdef
TypeError: 'tuple' object does not support item deletion
(0, 3)

delで範囲を削除する

del x[start:stop:step] で、リストの指定範囲をまとめて削除できます。

a = [0, 1, 2, 3, 4, 5]
del a[1:3]        # 1〜2番目を削除
print(a)

a = [0, 1, 2, 3, 4, 5]
del a[::2]        # 1つおきに削除
print(a)

a = [0, 1, 2, 3, 4, 5]
del a[-2:]        # 末尾2件を削除
print(a)

a = [0, 1, 2, 3, 4, 5]
del a[10:20]      # 範囲外でも例外にならず、何も起きない
print(a)

a = [0, 1, 2, 3, 4, 5]
del a[:]          # 全要素を削除(a.clear() と同じ結果)
print(a)

実行結果:

[0, 3, 4, 5]
[1, 3, 5]
[0, 1, 2, 3]
[0, 1, 2, 3, 4, 5]
[]
やりたいこと 書き方 補足
範囲を削除 del a[i:j] a[i:j] = [] と同じ結果
先頭n件を削除 del a[:n] 残したい側を a = a[n:] と書く方法もある
末尾n件を削除 del a[-n:] n が 0 だと全件が対象になる点は取り出しと同じ
全要素を削除 del a[:] a.clear() のほうが意図が伝わりやすい
変数ごと削除 del a リストの中身ではなく名前を消す操作

del a[:n] は同じリストを短くし、a = a[n:] は新しいリストを作って入れ直すという違いがあります。ほかの変数からも同じリストを見ている場合は、この違いが結果に表れます。

スライスは浅いコピーを作る

a[:] は新しいリストになる

スライスの結果は、元とは別の新しいリストです。a[:] で全体を取り出すと、リストのコピーとして使えます。

a = [1, 2, 3]
b = a[:]           # 全体のスライス = コピー
c = a              # 代入は同じリストを指すだけ

b.append(4)
c.append(99)

print(a)           # c 経由の変更は a にも表れる
print(b)           # b は別のリスト
print(b is a, c is a)

実行結果:

[1, 2, 3, 99]
[1, 2, 3, 4]
False True

入れ子のリストは中身を共有する

ただし、このコピーは浅いコピー(外側の入れ物だけを新しく作り、中の要素は同じものを指す)です。要素がリストや辞書のときは、内側の変更が両方に表れます。

import copy

matrix = [[1, 2], [3, 4]]
shallow = matrix[:]              # 浅いコピー
deep = copy.deepcopy(matrix)     # 深いコピー(内側も複製)

matrix[0][0] = 100               # 内側のリストを書き換える

print(matrix)
print(shallow)                   # 内側を共有しているので変わる
print(deep)                      # 内側も別物なので変わらない
print(shallow[0] is matrix[0], deep[0] is matrix[0])

実行結果:

[[100, 2], [3, 4]]
[[100, 2], [3, 4]]
[[1, 2], [3, 4]]
True False
コピーの方法 外側 内側の要素 向いている場面
b = a 同じもの 同じもの コピーではなく別名を付けたいとき
a[:]、a.copy()、list(a) 新しく作る 共有する 数値や文字列だけのリスト
copy.deepcopy(a) 新しく作る 新しく作る 入れ子のリスト・辞書を独立させたいとき

要素が数値や文字列だけなら、a[:] のコピーで十分です。 入れ子の構造を独立させたいときに copy.deepcopy を選びます。

文字列とタプルの全体スライスは同じオブジェクトが返る

文字列やタプルは変更できないため、コピーを作る必要がありません。CPython 3.14.7 で確かめると、s[:] や t[:] は元と同じオブジェクトをそのまま返しました。

s = "abcdef"
t = (1, 2, 3)
a = [1, 2, 3]

print(s[:] is s)   # 文字列
print(t[:] is t)   # タプル
print(a[:] is a)   # リストは別のオブジェクト

実行結果:

True
True
False

これは処理系の最適化による挙動で、プログラムの結果に影響はありません。「リストのスライスは必ず新しいリスト」とだけ覚えておけば足ります。

slice()オブジェクトで範囲に名前を付ける

角かっこの中身を変数にできる

x[1:4] の 1:4 の部分は、内部では slice(1, 4) というオブジェクトとして扱われます。これを自分で作って変数に入れておくと、同じ範囲を何度も使い回したり、範囲に名前を付けたりできます。

a = [0, 1, 2, 3, 4, 5]

head = slice(3)             # [:3] と同じ
middle = slice(1, 4)        # [1:4] と同じ
rev = slice(None, None, -1) # [::-1] と同じ

print(a[head], a[middle], a[rev])
print(middle.start, middle.stop, middle.step)
print("abcdef"[middle])     # 文字列にも同じ slice を使える

実行結果:

[0, 1, 2] [1, 2, 3] [5, 4, 3, 2, 1, 0]
1 4 None
bcd
コロンでの書き方 slice() での書き方
x[:3] x[slice(3)]
x[2:] x[slice(2, None)]
x[1:4] x[slice(1, 4)]
x[::2] x[slice(None, None, 2)]
x[::-1] x[slice(None, None, -1)]

省略する部分には None を書きます。slice(3) は range(3) と同じ考え方で、引数が1つのときは stop として扱われます。

固定長のデータを読みやすく切り出す

桁位置が決まっている文字列を扱うとき、数字を直接書くよりも名前付きの slice にすると、どこが何を表すかが一目でわかります。

# 備品コード:区分2桁 + 連番4桁 + 登録年月6桁
record = "PC0042202610"

KIND = slice(0, 2)
SERIAL = slice(2, 6)
YEAR_MONTH = slice(6, 12)

print(record[KIND])
print(int(record[SERIAL]))
print(record[YEAR_MONTH])

実行結果:

PC
42
202610

indices() で実際の位置を確かめる

slice.indices(長さ) は、その長さのシーケンスに当てはめたときの 実際の (start, stop, step) を返します。負の数や省略、範囲外の指定がどう解釈されるかを確かめたいときに便利です。

print(slice(None, None, -1).indices(6))   # [::-1] を長さ6に当てはめる
print(slice(-3, None).indices(6))         # [-3:]
print(slice(4, 100).indices(6))           # [4:100] は 6 に丸められる
print(slice(4, -1, -1).indices(6))        # [4:-1:-1] は 4 から 5 へ → 空

start, stop, step = slice(1, 100, 2).indices(6)
print(list(range(start, stop, step)))     # 取り出されるインデックス

実行結果:

(5, -1, -1)
(3, 6, 1)
(4, 6, 1)
(4, 5, -1)
[1, 3, 5]

[::-1] の stop が -1 と表示されるのは、「0番目の1つ手前」を表す内部的な値です。角かっこの中に自分で -1 と書いた場合は「末尾」と解釈されるため、同じにはなりません。逆順で先頭まで取るときに stop を省略する理由が、ここからもわかります。

文字列の切り出しレシピ

文字列のスライスは1文字単位で数えます。日本語の全角文字も1文字として扱われます。

先頭n文字・末尾n文字

title = "Pythonスライスの使い方"

print(title[:6])      # 先頭6文字
print(title[-3:])     # 末尾3文字
print(title[6:10])    # 7文字目から4文字
print(len(title))     # 全角も1文字として数える

実行結果:

Python
使い方
スライス
14

長い文字列を省略表示する

def shorten(text, width):
    """width 文字を超えるときは末尾を … に置き換える"""
    if len(text) <= width:
        return text
    return text[:width - 1] + "…"

print(shorten("スライスの使い方をまとめました", 10))
print(shorten("短い文", 10))

実行結果:

スライスの使い方を…
短い文

先頭・末尾の決まった文字を取り除く

filename = "report_2026.csv"

print(filename[:-4])                      # 末尾4文字を除く
print(filename[-4:])                      # 末尾4文字(拡張子)
print(filename.removesuffix(".csv"))      # 末尾が一致するときだけ除く
print(filename.removeprefix("report_"))   # 先頭が一致するときだけ除く

other = "report_2026.json"
print(other[:-4])                         # 文字数で切ると意図とずれる
print(other.removesuffix(".csv"))         # 一致しないのでそのまま

実行結果:

report_2026
.csv
report_2026
2026.csv
report_2026.
report_2026.json

「文字数」で切りたいならスライス、「この文字列が付いていたら除く」なら removeprefix() / removesuffix() と使い分けると、意図に合った結果になります(この2つのメソッドは Python 3.9 以降で使えます)。

区切り文字の位置で切り出す

address = "user@example.com"
pos = address.find("@")

print(address[:pos])       # @ より前
print(address[pos + 1:])   # @ より後

date = "2026-10-03"
print(date[:4], date[5:7], date[8:])   # 桁位置が決まっているとき

実行結果:

user
example.com
2026 10 03
やりたいこと 書き方 例の結果
先頭n文字 s[:n] 'Py'
末尾n文字 s[-n:] 'on'
先頭n文字を除く s[n:] 'thon'
末尾n文字を除く s[:-n] 'Pyth'
逆順 s[::-1] 'nohtyP'
1文字おき s[::2] 'Pto'
回文かどうか s == s[::-1] True("level" の場合)

応用レシピ

リストをn個ずつに分割する

data = list(range(1, 11))
size = 4

chunks = [data[i:i + size] for i in range(0, len(data), size)]
print(chunks)

実行結果:

[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]

最後のかたまりが size に満たなくても、範囲外を丸めてくれるおかげで、そのまま残りが入ります。Python 3.12 以降では itertools.batched() でも同じ分割ができます(こちらはタプルで返ります)。

from itertools import batched

print(list(batched(range(1, 11), 4)))

実行結果:

[(1, 2, 3, 4), (5, 6, 7, 8), (9, 10)]

前半と後半に分ける・並びをずらす

a = [1, 2, 3, 4, 5, 6, 7]

mid = len(a) // 2
print(a[:mid], a[mid:])       # 前半と後半

k = 2
print(a[k:] + a[:k])          # 左に2つずらす
print(a[-k:] + a[:-k])        # 右に2つずらす

実行結果:

[1, 2, 3] [4, 5, 6, 7]
[3, 4, 5, 6, 7, 1, 2]
[6, 7, 1, 2, 3, 4, 5]

隣り合う要素をペアにする

points = [3, 8, 6, 10]

for prev, cur in zip(points, points[1:]):
    print(prev, "→", cur, "差", cur - prev)

実行結果:

3 → 8 差 5
8 → 6 差 -2
6 → 10 差 4

points[1:] で1つずらしたリストを作り、zip で並べると「前の値と今の値」を同時に扱えます。

rangeやbytesもスライスできる

スライスは「順番に並んだ型(シーケンス)」に共通の機能です。range や bytes にもそのまま使えます。

r = range(10)
print(r[2:8:2])            # range をスライスすると range が返る
print(list(r[::-1]))

b = b"\x89PNG\r\n"
print(b[1:4])              # bytes は bytes が返る

実行結果:

range(2, 8, 2)
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
b'PNG'

一方、順番という考え方を持たない集合(set)や辞書(dict)にはスライスを使えません。

try:
    {1, 2, 3}[0:2]
except TypeError as e:
    print("TypeError:", e)

d = {"a": 1, "b": 2, "c": 3}
try:
    d[0:2]
except Exception as e:
    print(type(e).__name__ + ":", e)

print(list(d.items())[:2])              # いったんリストにしてからスライス

from itertools import islice
print(dict(islice(d.items(), 2)))       # islice なら全体をリストにせずに済む

実行結果:

TypeError: 'set' object is not subscriptable
KeyError: slice(0, 2, None)
[('a', 1), ('b', 2)]
{'a': 1, 'b': 2}
sample.py:2: SyntaxWarning: 'set' object is not subscriptable; perhaps you missed a comma?
  {1, 2, 3}[0:2]

辞書の先頭n件がほしいときは、list(d.items())[:n] か itertools.islice を使います。

よくあるエラーと直し方

実際に出力されたエラー文と、直し方をまとめます。

エラー文(Python 3.14.7 での出力) 起きる書き方 直し方
TypeError: slice indices must be integers or None or have an __index__ method a[1.0:2]、a[n/2:] // で割るか int() で整数にする
TypeError: slice indices must be integers or None or have an __index__ method a["1":] int("1") に変換してから使う
ValueError: slice step cannot be zero a[::0] step は 0 以外にする
IndexError: list index out of range a[5](インデックス指定) a[5:6] にすると空のリストが返る
TypeError: 'str' object does not support item assignment s[1:3] = "XY" s[:1] + "XY" + s[3:] で作り直す
TypeError: 'tuple' object does not support item deletion del t[1:3] t[:1] + t[3:] で作り直す
ValueError: attempt to assign sequence of size 2 to extended slice of size 3 a[::2] = ["x", "y"] 右辺の個数を左辺に合わせる
TypeError: 'set' object is not subscriptable {1, 2, 3}[0:2] sorted(s)[:2] などリストにしてから

一番よく出会うのは1行目の「slice indices must be integers」です。割り算 / の結果は割り切れても float になるため、位置の計算には // を使います。

a = [1, 2, 3, 4, 5, 6]

try:
    print(a[:len(a) / 2])        # / の結果は 3.0(float)
except TypeError as e:
    print("TypeError:", e)

print(a[:len(a) // 2])           # // なら 3(int)

実行結果:

TypeError: slice indices must be integers or None or have an __index__ method
[1, 2, 3]

書く前のチェックリスト

確認すること 目安
stop の位置は「含まない」で合っているか n番目まで含めたいなら stop は n + 1
件数が変数のとき、0 になる可能性はあるか 末尾n件は x[len(x) - n:]
step が負のとき、start が後ろ側になっているか 先頭まで含めるなら stop は省略
位置の計算に / を使っていないか // で整数にする
元のリストを書き換えたいのか、新しく作りたいのか 書き換えは a[:] = ...、新規は a = ...
入れ子のリストをコピーしていないか 独立させるなら copy.deepcopy
結果が空でも気づける作りになっているか 必要なら if not result: で確認

よくある質問

Q. スライスで範囲外を指定してもエラーにならないのはなぜですか? A. スライスは「この範囲にある要素を集めたもの」と定義されていて、範囲からはみ出した位置は自動的に先頭または末尾に丸められるためです。該当する要素がなければ空のリスト(文字列なら空文字列)が返ります。1件だけを取り出すインデックス指定 a[10] は、要素が存在しないと値を返せないので IndexError になります。

Q. [::-1] と reversed() はどちらを使えばよいですか? A. 逆順にした結果を同じ型(リストや文字列)としてそのまま使いたいなら [::-1]、for文で後ろから順にたどるだけなら reversed() が向いています。reversed() は新しいリストを作らずに1つずつ取り出すので、要素数が多いときにメモリを使いません。リスト自体を逆順に並べ替えたいなら list.reverse() です。

Q. スライスで取り出したリストを変更すると、元のリストも変わりますか? A. 追加・削除・要素の入れ替えは元のリストに影響しません。スライスは新しいリストを作るためです。ただし浅いコピーなので、要素がリストや辞書の場合に「その要素の中身」を書き換えると、元のリストからも同じ変更が見えます。独立させたいときは copy.deepcopy() を使います。

Q. 文字列の一部をスライスで書き換えられますか? A. 文字列は変更できない型なので、s[1:3] = "XY" は TypeError になります。s[:1] + "XY" + s[3:] のように前後を取り出してつなぎ、変数に入れ直します。同じ文字を別の文字に置き換えるだけなら str.replace() も使えます。

まとめ

  • スライスは x[start:stop:step]。stop の位置は含まず、3つとも省略できる
  • 先頭n件は [:n]、末尾n件は [-n:]、1つおきは [::2]、逆順は [::-1]。リスト・文字列・タプルで同じ書き方が使え、元と同じ型が返る
  • 範囲外を指定しても IndexError にならず、端に丸められる。そのぶん、位置を間違えると空の結果が静かに返る点に気を配る
  • 負の step では start を後ろ側に置き、先頭まで含めるときは stop を省略する(-1 と書くと末尾の意味になる)
  • リストはスライス代入と del で、置き換え・挿入・削除をまとめて書ける。step つきの代入は個数をそろえる
  • a[:] は浅いコピー。入れ子の構造を独立させるなら copy.deepcopy
  • slice() オブジェクトで範囲に名前を付けると、桁位置の決まったデータを読みやすく扱える

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Pythonスライスの使い方早見表|リスト・文字列の切り出しと逆順」

株式会社RJCでは、Pythonのスライスのような基礎文法を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?