リストのスライスの罠

リストの部分リストを得るとき、つぎのようにします。

a = range(5)    # [0, 1, 2, 3, 4]
a[1:3]          # [1, 2]

range(1, 3)に対応しています。同様にステップ幅も指定できます。

a[1:4:2]        # [1, 3]
a[3:0:-1]       # [3, 2, 1]

これもrange(1, 4, 2)、range(3, 0, -1)に対応しています。
最初と次の項を省略すると最初からと最後までと解釈されます。

a[2:]           # [2, 3, 4]
a[3::-1]        # [3, 2, 1, 0]

両方省略することもできます。

a[:]            # [0, 1, 2, 3, 4]
a[::-1]         # [4, 3, 2, 1, 0]

それぞれ、copyとreverseになっています。

さて、ここからが本題です。次のようなコードを組みたいとします。リストからインデックスkとlの間を取り出します。例えば、[0, 1, 4, 9, 16]で1, 3なら[1, 4, 9]、3, 1なら[9, 4, 1]となります。
k < lとそれ以外の場合わけが要ります。

if k < l:
    return a[k:l+1]
else:
    return a[k:l-1:-1]

しかし、これには罠があります。l = 0のとき、

a[2:0-1:-1]     # []

-1は末尾の要素を指すため、a[2:4:-1]と同じになってしまうからです。そのためさらに場合わけが必要です。

if k < l:
    return a[k:l+1]
elif l == 0:
    return a[k::-1]
else:
    return a[k:l-1:-1]

しかし、これよりもreverseする方が短く書けます。

if k < l:
    return a[k:l+1]
else:
    return a[l:k+1][::-1]