Pythonのスライスによるリストや文字列の部分選択・代入
Pythonではコロンを使って表すスライス(例: [2:5:2]
)によって、リストや文字列、タプルなどのシーケンスオブジェクトの一部分を選択して取得したり別の値を代入したりできる。
スライスの基本的な使い方
開始位置startと終了位置stopを指定
スライスでは選択範囲の開始位置start
と終了位置stop
を[start:stop]
のように書く。
start <= x < stop
の範囲が選択される。start
番目の値は含まれるがstop
番目の値は含まれない。
l = [0, 10, 20, 30, 40, 50, 60]
print(l[2:5])
# [20, 30, 40]
スライスで指定する位置(インデックス)は要素と要素の間を指す、と考えると理解しやすい。
スライスの使い方をおぼえる良い方法は、インデックスが文字と文字の あいだ (between) を指しており、最初の文字の左端が 0 になっていると考えることです。 3. 形式ばらない Python の紹介 - 文字列型 (string) — Python 3.11.3 ドキュメント
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
上は文字列の例だが、リストやタプルなどでも要素と要素の間を指すと考えればよい。負の値については後述。
開始位置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
とした場合、奇数個目または偶数個目の要素のみを取得できる。
l = [0, 10, 20, 30, 40, 50, 60]
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
を負の値で指定すると、末尾からの位置となる。
スライスで指定する位置(インデックス)の考え方を再掲する。要素間を指すイメージ。
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
l = [0, 10, 20, 30, 40, 50, 60]
print(l[3:-1])
# [30, 40, 50]
print(l[-2:])
# [50, 60]
print(l[-5:-2])
# [20, 30, 40]
増分stepをマイナスで指定
増分step
を負の値で指定すると、後ろから逆順で要素を取得する。
start
から逆向きに値を取得していくため、start
のほうがstop
より後ろの位置を示していないと空になってしまうので注意。
l = [0, 10, 20, 30, 40, 50, 60]
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]
step
が負の値の場合、start
を省略すると末尾から、stop
を省略すると先頭までが選択される。両方とも省略するとすべての要素が選択される。
start
とstop
を省略しstep
を-1
とすることで、もとのオブジェクトの順番を逆転したオブジェクトが取得できる。
print(l[:3:-1])
# [60, 50, 40]
print(l[3::-1])
# [30, 20, 10, 0]
print(l[::-1])
# [60, 50, 40, 30, 20, 10, 0]
逆順に並べ替えるにはほかにreverse()
, reversed()
を使う方法がある。詳細は以下の記事を参照。
slice関数によるスライスオブジェクトの生成
組み込み関数slice()
を使うとスライスオブジェクトを生成できる。同じ位置の要素を繰り返し取得したい場合はスライスオブジェクトを一度生成しておくだけでよいので便利。
slice(start, stop, step)
はstart:stop:step
に等しい。
l = [0, 10, 20, 30, 40, 50, 60]
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
となる。全体を表すスライス:
をslice()
関数で生成したい場合は明示的にNone
を指定する。
# sl = slice()
# TypeError: slice expected at least 1 arguments, got 0
sl = slice(None)
print(sl)
# slice(None, None, None)
print(l[sl])
# [0, 10, 20, 30, 40, 50, 60]
スライスによる値の代入
スライスで選択した範囲に値を代入できる。
スライスで選択した範囲の要素数と代入する要素数(右辺のオブジェクトの要素数)は一致していなくてもよい。
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
となる。上の例のように、要素数が1つのリストなどを指定すればよい。
# l[2:3] = 200
# TypeError: can only assign an iterable
l[2:3] = [200]
print(l)
# [0, 10, 200, 50, 60]
右辺が空だとスライスで選択した範囲の要素が削除される。
l[1:4] = []
print(l)
# [0, 60]
範囲外や空の範囲を指定して代入することもできる。指定した位置に右辺の値が追加・挿入される。
l = [0, 10, 20, 30, 40, 50, 60]
l[100:200] = [-1, -2, -3]
print(l)
# [0, 10, 20, 30, 40, 50, 60, -1, -2, -3]
l[2:2] = [-100]
print(l)
# [0, 10, -100, 20, 30, 40, 50, 60, -1, -2, -3]
増分step
を指定した飛び飛びの範囲に対しては要素数が等しくないとエラーValueError
となる。
l = [0, 10, 20, 30, 40, 50, 60]
l[1::2] = [100, 200, 300]
print(l)
# [0, 100, 20, 200, 40, 300, 60]
# l[1::2] = [100, 200]
# ValueError: attempt to assign sequence of size 2 to extended slice of size 3
なお、リストの途中や末尾に要素を追加する場合はinsert()
, append()
などのメソッドが用意されているので、そちらを使ったほうがコードの可読性は高い。
- 関連記事: Pythonでリスト(配列)に要素を追加するappend, extend, insert
- 関連記事: Pythonでリスト(配列)の要素を削除するclear, pop, remove, del
二次元配列(リストのリスト)の場合
リストのリストで構成された二次元配列にスライスを適用する場合、あくまでもリストを要素としたリストなので、スライスによって選択されるのは要素であるリスト。
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]]
選択されたリストにさらにスライスを適用するにはリスト内包表記を使う。
- 関連記事: Pythonリスト内包表記の使い方
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では[1:4, 2:5]
のように各次元のスライスをカンマで区切って指定できる。
変数に代入した場合の浅いコピーと深いコピー
スライスで取得した結果は浅いコピーとなる。浅いコピーと深いコピーについての詳細は以下の記事を参照。
例えば、数値などのイミュータブルオブジェクトを要素とするリストの場合は、スライスで取得した結果を変数に代入しその変数の要素を更新しても元のオブジェクトは変更されない。
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]]
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()
を使う。
import copy
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[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[2:5])
# (20, 30, 40)
# t[2:5] = (200, 300, 400)
# TypeError: 'tuple' object does not support item assignment
文字列の分割や置換については以下の記事を参照。