Pythonのスライスによるリストや文字列の部分選択・代入

Pythonではコロンを使って表すスライス(例: [2:5:2])によって、リストや文字列、タプルなどのシーケンスオブジェクトの一部分を選択して取得したり別の値を代入したりできます。 以下の内容について説明します。

スライスの基本的な使い方 開始位置startと終了位置stopを指定 増分stepを指定 マイナスの値で後ろから指定 開始位置startと終了位置stopをマイナスで指定 増分stepをマイナスで指定 slice()関数によるスライスオブジェクトの生成 スライスによる値の代入 二次元配列(リストのリスト)の場合 変数に代入した場合の浅いコピーと深いコピー 文字列やタプルの場合

スライスの基本的な使い方

開始位置startと終了位置stopを指定

スライスでは選択範囲の開始位置startと終了位置stopを[start:stop]のように書きます。 start <= x < stopの範囲が選択されます。start番目の値は含まれるがstop番目の値は含まれないので気をつけてください。

l = [0, 10, 20, 30, 40, 50, 60]
print(l)
# [0, 10, 20, 30, 40, 50, 60]

print(l[2:5])
# [20, 30, 40]

開始位置startを省略した場合は最初から、終了位置stopを省略した場合は最後までが選択されます。両方とも省略した場合はすべての値が選択されます。

print(l[:3])
# [0, 10, 20]

print(l[3:])
# [30, 40, 50, 60]

print(l[:])
# [0, 10, 20, 30, 40, 50, 60]

範囲外を指定した場合 要素数を超える位置を指定してもエラーにはならず無視されます。負の値を指定した場合については後述します。

print(l[2:10])
# [20, 30, 40, 50, 60]

空のリストが返る場合 どの要素も選択されないstartとstopを指定した場合もエラーにはならない。空のリストが返ってきます。

print(l[5:2])
# []

print(l[2:2])
# []

print(l[10:20])
# []

増分stepを指定

開始位置startと終了位置stopに加えて、増分stepも指定できます。[start:stop:step]のように書きます。 例えばstepを2とした場合、奇数個目の要素のみ、または偶数個目の要素のみを取得できます。

print(l[::2])
# [0, 20, 40, 60]

print(l[1::2])
# [10, 30, 50]

そのほかの例を示します。

print(l[::3])
# [0, 30, 60]

print(l[2:5:2])
# [20, 40]

これまでの例のように、省略した場合はstep=1となります。

マイナスの値で後ろから指定

開始位置startと終了位置stopをマイナスで指定

開始位置startと終了位置stopを負の値で指定すると、末尾からの位置となります。 -1が最後の要素を示す。 stop=-1とするとstop番目の値は含まれないため後ろから2番めの値までが選択されます。

print(l[3:-1])
# [30, 40, 50]

そのほかの例を示します。

print(l[-2:])
# [50, 60]

print(l[-5:-2])
# [20, 30, 40]

増分stepをマイナスで指定

増分stepを負の値で指定すると、後ろから逆順で要素を取得します。 startから逆向きに値を取得していくため、startのほうがstopより後ろの位置を示していないと空になってしまうので気をつけてください。

print(l[5:2:-1])
# [50, 40, 30]

print(l[2:5:-1])
# []

そのほかの例を示します。

print(l[-2:-5:-1])
# [50, 40, 30]

print(l[-2:2:-1])
# [50, 40, 30]

print(l[5:2:-2])
# [50, 30]

startとstopを省略しstepを-1とすることで、もとのオブジェクトの順番を逆転したオブジェクトが取得できます。

print(l[::-1])
# [60, 50, 40, 30, 20, 10, 0]

逆順に並べ替えるにはほかにreverse(), reversed()を使う方法があります。詳細は

slice関数によるスライスオブジェクトの生成

組み込み関数slice()を使うとスライスオブジェクトを生成できます。同じ位置の要素を繰り返し取得したい場合はスライスオブジェクトを一度生成しておくだけでよいので便利です。

  1. 組み込み関数slice() — Python 3.6.5 ドキュメント

slice(start, stop, step)はstart:stop:stepに等しい。

sl = slice(2, 5, 2)
print(sl)
# slice(2, 5, 2)

print(type(sl))
# <class 'slice'>

print(l[sl])
# [20, 40]

引数を2つ指定した場合はstep=Noneとなりstart:stopと等価。

sl = slice(2, 5)
print(sl)
# slice(2, 5, None)

print(l[sl])
# [20, 30, 40]

引数を1つだけ指定した場合はstart=None, step=Noneとなり:stopと等価。

sl = slice(2)
print(sl)
# slice(None, 2, None)

print(l[sl])
# [0, 10]

引数をすべて省略するとエラーTypeErrorとなります。

# sl = slice()
# TypeError: slice expected at least 1 arguments, got 0

全体を表すスライス:をslice()関数で生成したい場合は明示的にNoneを指定します。

sl = slice(None)
print(sl)
# slice(None, None, None)

print(l[sl])
# [0, 10, 20, 30, 40, 50, 60]

スライスによる値の代入

スライスで選択した範囲に値を代入することができます。 スライスで選択した範囲の要素数と代入する要素数(右辺のオブジェクトの要素数)は一致していなくても問題ありません。

print(l)
# [0, 10, 20, 30, 40, 50, 60]

l[2:5] = [200, 300, 400]
print(l)
# [0, 10, 200, 300, 400, 50, 60]

l[2:5] = [-2, -3]
print(l)
# [0, 10, -2, -3, 50, 60]

l[2:4] = [2000, 3000, 4000, 5000]
print(l)
# [0, 10, 2000, 3000, 4000, 5000, 50, 60]

l[2:6] = [20000]
print(l)
# [0, 10, 20000, 50, 60]

右辺にはリストなどのイテラブルオブジェクトのみ指定できます。スカラー値を指定するとエラーTypeErrorとなります。

# l[2:3] = 200
# TypeError: can only assign an iterable

右辺が空だとスライスで選択した範囲の要素が削除されます。

l[1:4] = []
print(l)
# [0, 60]

空の範囲に対しても代入できます。指定した位置に右辺の値が挿入されます。

l[20:60] = [-1, -2, -3]
print(l)
# [0, 60, -1, -2, -3]

l[2:2] = [-100]
print(l)
# [0, 60, -100, -1, -2, -3]

増分stepを指定した飛び飛びの範囲に対しては要素数が等しくないとエラーValueErrorとなります。

print(l[:5:2])
# [0, -100, -2]

l[:5:2] = [100, 200, 300]
print(l)
# [100, 60, 200, -1, 300, -3]

# l[:5:2] = [100, 200]
# ValueError: attempt to assign sequence of size 2 to extended slice of size 3

なお、リストの途中や末尾に要素を追加する場合はinsert(), append()などのメソッドが用意されているので、そちらを使ったほうがコードの可読性は高い。

二次元配列(リストのリスト)の場合

リストのリストで構成された以下のような二次元配列にスライスを適用したい場合です。

l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
print(l_2d)
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

あくまでもリストを要素としたリストなので、スライスによって選択されるのは要素であるリスト。

print(l_2d[1:3])
# [[3, 4, 5], [6, 7, 8]]

選択されたリストにさらにスライスを適用するにはリスト内包表記を使います。

print([l[:2] for l in l_2d[1:3]])
# [[3, 4], [6, 7]]

列を取得したい場合は転置する方法もあります。

l_2d_t = [list(x) for x in zip(*l_2d)]
print(l_2d_t)
# [[0, 3, 6, 9], [1, 4, 7, 10], [2, 5, 8, 11]]

print(l_2d_t[1])
# [1, 4, 7, 10]

なお、配列のサイズや実現したいことにもよるが、NumPyをインストールできる環境であれば多次元配列の操作はNumPyを使うほうが楽。 NumPyではarr[1:4, 2:5]のように各次元のスライスをカンマで区切って指定できます。

変数に代入した場合の浅いコピーと深いコピー

スライスで取得した結果は浅いコピーとなります。 例えば、数値のリストの場合などは、スライスで取得した結果を変数に代入しその変数の要素を更新しても元のオブジェクトは変更されません。

l = [0, 10, 20, 30, 40, 50, 60]
print(l)
# [0, 10, 20, 30, 40, 50, 60]

l_slice = l[2:5]
print(l_slice)
# [20, 30, 40]

l_slice[1] = 300
print(l_slice)
# [20, 300, 40]

print(l)
# [0, 10, 20, 30, 40, 50, 60]

要素としてリストなどを含んでいる複合オブジェクトの場合は、リストの要素を更新すると元のオブジェクトも変更されます。

l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
print(l_2d)
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

l_2d_slice = l_2d[1:3]
print(l_2d_slice)
# [[3, 4, 5], [6, 7, 8]]

l_2d_slice[0][1] = 400
print(l_2d_slice)
# [[3, 400, 5], [6, 7, 8]]

print(l_2d)
# [[0, 1, 2], [3, 400, 5], [6, 7, 8], [9, 10, 11]]

これを防ぐためには標準ライブラリのcopyモジュールをインポートし、deepcopy()を使います。

8.10. copy — 浅いコピーおよび深いコピー操作 — Python 3.6.5 ドキュメント

import copy

l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
print(l_2d)
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

l_2d_slice_deepcopy = copy.deepcopy(l_2d[1:3])
print(l_2d_slice_deepcopy)
# [[3, 4, 5], [6, 7, 8]]

l_2d_slice_deepcopy[0][1] = 400
print(l_2d_slice_deepcopy)
# [[3, 400, 5], [6, 7, 8]]

print(l_2d)
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

文字列やタプルの場合

これまではリスト(list型)の例を示してきたが、文字列やタプルなどほかのシーケンスオブジェクトでも同様にスライスが使えます。 ただし、文字列、タプルはイミュータブル(更新不可)なので代入はできません。

s = 'abcdefg'
print(s)
# abcdefg

print(s[2:5])
# cde

print(s[::-1])
# gfedcba

# s[2:5] = 'CDE'
# TypeError: 'str' object does not support item assignment

t = (0, 10, 20, 30, 40, 50, 60)
print(t)
# (0, 10, 20, 30, 40, 50, 60)

print(t[2:5])
# (20, 30, 40)

# t[2:5] = (200, 300, 400)
# TypeError: 'tuple' object does not support item assignment
Last Updated: 6/26/2019, 10:34:03 PM