web-dev-qa-db-ja.com

Python list error:[::-1] step on [:-1] slice

私はPythonでリストスライシングの基本を理解していると思っていましたが、次のようにスライスでネガティブステップを使用しているときに予期しないエラーを受け取っていました:

>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:-1]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-1:-1]
[]

(これはPython 3.5)で実行されていることに注意してください)

A [:-1:-1]がa [::-1]を使用してリスト全体と同じ方法でa [:-1]スライスを逆方向にステップしないのはなぜですか?

List.reverse()も使用できることを理解していますが、基になるpythonスライス機能をよりよく理解しようとしています。

17
Matt Kelty

_-1_の最初の_a[:-1:-1]_は、あなたが思うことを意味しません。

スライスでは、負の開始/終了インデックスは文字どおりに解釈されません。代わりに、リストの末尾を簡単に参照するために使用されます(つまり、len(a)に関連しています)。これは、スライスの方向に関係なく起こります。

この意味は

_a[:-1:-1]
_

と同等です

_a[:len(a)-1:-1]
_

逆スライス中に省略された場合、開始インデックスのデフォルトはlen(a)-1になり、上記と同等になります

_a[len(a)-1:len(a)-1:-1]
_

開始インデックスと終了インデックスは同じであり、終了インデックスは排他的であるため、これは常に空のリストになります。

0番目の要素まで逆向きにスライスするには、次の表記法のいずれかを使用できます。

_>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:None:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[:-len(a)-1:-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
_
22
NPE

_[1, 2, 3, ...][1:4:1]_と入力すると、[1, 2, 3, ...][slice(1, 4, 1)]と同じになります。したがって、_1:4:1_はsliceオブジェクトの省略形です。 sliceシグネチャはslice(stop)またはslice(start, stop[, step])であり、引数にNoneを使用することもできます。

_:: -> slice(None, None, None)
:4 -> slice(4)
# and so on
_

_[a: b: c]_があるとします。 インデックスのルールは次のようになります。

  1. 最初のcがチェックされます。デフォルトは_+1_で、cの符号はステップの順方向または逆方向を示します。 cの絶対値は、ステップサイズを示します。
  2. aがチェックされている。 cが正またはNoneの場合、aのデフォルトは_0_です。 cが負の場合、aのデフォルトは_-1_です。
  3. 最後にbがチェックされます。 cが正またはNoneの場合、bのデフォルトはlenです。 cが負の場合、bのデフォルトは-(len+1)です。

注1:Pythonの縮退スライスは適切に処理されます:

  • 大きすぎるまたは小さすぎるインデックスは、lenまたは_0_に置き換えられます。
  • 上限が下限よりも小さい場合、空のリストまたは文字列またはその他の値(正のcの場合)が返されます。

注2:大まかに言うと、Pythonは、この条件_(a < b) if (c > 0) else (a > b)_がTrue(すべてのステップで_a += c_を更新します。また、すべての負のインデックスは_len - index_に置き換えられます。

このルールとメモを組み合わせると、空のリストを取得した理由がわかります。あなたの場合:

_ In[1]: [1, 2, 3, 4, 5, 6][:-1:-1]        # `c` is negative so `a` is -1 and `b` is -1
Out[1]: [] 

# it is the same as:

 In[2]: [1, 2, 3, 4, 5, 6][-1: -1: -1]    # which will produce you an empty list 
Out[2]: [] 
_

スライス表記について非常に良い議論があります: Pythonのスライス表記を説明してください

8
godaygo

私は通常、range-オブジェクトをスライスすると便利だと思います(これはpython3でのみ可能です-python2ではrangelistを生成し、xrangeはできません特定の長さのリストにどのインデックスが使用されているかを確認する必要がある場合:

>>> range(10)[::-1]  
range(9, -1, -1)

>>> range(10)[:-1]  
range(0, 9)

そして最後のケースでは:

>>> range(10)[:-1:-1]
range(9, 9, -1)

これは何が起こったのかも説明しています。最初のインデックスは9ですが、9はストップインデックス9より低くありません(pythonでストップインデックスはexcludedであることに注意してください)。素子。

インデックスは連続して適用することもできます:

>>> list(range(10))[::-1][:-1]  # first reverse then exclude last item.
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> list(range(10))[:-1][::-1]  # other way around
[8, 7, 6, 5, 4, 3, 2, 1, 0]
4
MSeifert

sliceは、range引数を負の数にすると、step引数とstart引数が反対方向に機能するという点で、stopと同様に機能します。

>>> list(range(9, -1, -1)) == a[::-1]
True

これをより明確にするのに役立ついくつかの例:

>>> a[6:2:-2]
[6, 4]
>>> a[0:None:1] == a[::]
True
>>> a[-1:None:-1] == a[::-1]
True
>>> a[-2:None:-1] == a[:-1][::-1]
True
1
John B

Pythonのスライスは、最初はかなり単純に見えますが、実際の動作は かなり複雑 です(注3と5が関連しています)。スライス_a[i:j:k]_がある場合:

  • iまたはjが負の場合、aの最後からインデックスを参照します(したがって、_a[-1]_はaの最後の要素を参照します)
  • iまたはjが指定されていない場合、またはNoneである場合、デフォルトはaの終わりですが、which終了はkの符号に依存します:

    • kが正の場合、前方にスライスしているため、iは0になり、jlen(a)になります
    • kが負の場合、後方にスライスしているので、ilen(a)になり、jaの開始前の要素になります。

      NB:jcannotは-1に置き換えられます。Pythonは、jを_a[0]_の前の(存在しない)要素ではなく、alast要素として処理します。目的の動作を取得するには、jの代わりに-len(a)-1(または-(len(a)+1))を使用する必要があります。つまり、_a[j]_に到達するには、スライスはaの最後の要素は、len(a)要素のために左に移動し、さらにもう1つの要素を残して、aが開始する前に終了し、スライスに_a[0]_を含めます。

したがって、_a[:-1:-1]_は、「aの最後から_a[-1]_(iが指定されておらず、kが負であるため)の最後から最後の要素に移動することを意味しますof a(_j == -1_以降)、ステップサイズ-1 "。 ijは等しい-同じ場所でスライスを開始および停止するため、式は空のリストに評価されます。

_a[:-1]_を逆にするには、_a[-2::-1]_を使用できます。この方法では、スライスは最後から2番目の要素_a[-2]_から始まり(_a[:-1]_には_a[-1]_が含まれないため)、_a[0]_の「前」の要素まで後方に移動します。つまり、_a[0]_はスライスに含まれています。

_>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:-1]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> a[-2::-1]
[8, 7, 6, 5, 4, 3, 2, 1, 0]
_
1
Josh