スライス操作あれこれ
この記事は Python Tips Advent Calendar 2012 3日目の記事です。
今日は Python のスライス操作についてです。
基本的なスライス操作
>>> xs = range(10) >>> xs[2:5] # 2以上5未満までのインデックス [2, 3, 4] >>> xs[:2] # 前を省略した場合は0と同じ [0, 1] >>> xs[7:] # 後ろを省略した場合はlen(xs)、つまり10と同じ [7, 8, 9] >>> xs[-3:] # -3 は後ろから 3 番目(つまり len(xs)-3 と同じ) [7, 8, 9] >>> xs[-3:-1] # 当然 2 個目にもマイナスを入れられる [7, 8] >>> xs[::4] # 3 個目にはスキップ数が入れられる [0, 4, 8] >>> xs[2:-2:3] # 組み合わせ [2, 5] >>> xs[::-4] # マイナス値の場合は後ろからスキップする [9, 5, 1] >>> xs[8:1:-2] # 組み合わせ [8, 6, 4, 2]
スライスでこんな風にリストを取得できます。
スライス操作で書き換える
スライスで取得したリストに代入をすると、その要素が代入したリストに置き換わります。
>>> xs = range(10) >>> xs[:5] = [42] # [0, 1, 2, 3, 4] が [42] に置き換わる >>> print xs [42, 5, 6, 7, 8, 9] >>> xs[:1] = range(5) # [42] が [0, 1, 2, 3, 4] に置き換わる >>> print xs [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
要素の削除についても同様で、スライスで取得した要素が全て削除されます。
>>> del xs[::2] # [0, 2, 4, 6, 8] が削除される >>> print xs [1, 3, 5, 7, 9]
スキップ付きのスライスも同様です。スライスで取得した要素が代入したリストに置き換わります。
>>> xs[::2] = xs[::-2] # [1, 5, 9] が [9, 5, 1] に置き換わる >>> print xs [9, 3, 5, 7, 1]
ただしスキップ付きは注意点があって、これは要素数が同じでない場合は例外を投げます。
>>> xs[::2] = xs #doctest: +ELLIPSIS Traceback (most recent call last): ... ValueError: attempt to assign sequence of size 5 to extended slice of size 3
リストの反転
リストの反転は、スライスを使って簡単に書けます。
>>> xs = [1,2,3] >>> list(reversed(xs)) [3, 2, 1] >>> xs[::-1] # 短く書けるよ! [3, 2, 1]
リストのコピー
リストの(浅い)コピーもスライスで書けます。
>>> xs = [1,2,3] >>> list(xs) [1, 2, 3] >>> xs[:] # 短く書けるよ! [1, 2, 3]
スライスの正体
スライスというのは単なる略記法で、実際はこれは以下の様な slice クラスのオブジェクトになっています。
>>> xs = [1,2,3,4] >>> xs[1::2] [2, 4] >>> xs[slice(1, None, 2)] [2, 4]
slice クラスは start, stop, step の3つのプロパティを持っているので、以下のように自分でスライス処理を書いたりもできます。
>>> class X(object): ... def get(self, x): ... if isinstance(x, slice): ... return 'sliced object' ... else: ... return 'normal object' >>> x = X() >>> x.get(slice(1, 2, 3)) 'sliced object' >>> x.get(3) 'normal object'
ちなみに x.get(1:2:3) という書き方はできません。スライスの略記は [] の中だけで使えます。
Ellipsis
[] の中では、スライス以外にも、もう一つ、Ellipsis というクラスの略記法が使えます。
>>> class X(object): ... def __getitem__(self, x): ... if x is Ellipsis: ... return 'ellipsis object' ... else: ... return 'normal object' >>> x = X() >>> x[...] # Ellipsis を渡す 'ellipsis object' >>> x[Ellipsis] # x[...] はこれと同じ意味 'ellipsis object' >>> x[42] 'normal object'
これは標準のリストでは使われていないですが、例えば行列とかを計算してくれる numpy パッケージでは、こんな風に使えます。
>>> import numpy >>> x = numpy.array([[[1],[2],[3]], [[4],[5],[6]]]) >>> x[..., 0] array([[1, 2, 3], [4, 5, 6]]) >>> x[:, :, 0] # x[..., 0] はこれと同じ意味 array([[1, 2, 3], [4, 5, 6]])
.