>>>"helloworld"[::1]
'helloworld'
>>>"helloworld"[::-1]
'dlrowolleh'
構文によるとstr[start:end:step]
。どちらの場合も、開始はデフォルトで0です。最初の場合、文字列はインデックス値0から出力されますが、2番目の場合、文字列はインデックス値-1から出力されます。
そして私の質問は、後者の場合、なぜ文字列が-1から出力されるのか、そしてなぜそうなるのかということです。
ドキュメントによると(強調が追加されました):
ステップkでのiからjへのsのスライスは、0 <= n <(ji)/kとなるようなインデックスx= i + n*kのアイテムのシーケンスとして定義されます。つまり、インデックスはi、i + k、i + 2 * k、i + 3 * kなどであり、jに達すると停止します(ただし、jは含まれません)。iまたはjがlen(s)より大きい場合は、len(s)を使用します。iまたはjが省略されているか、Noneの場合、それらは「終了」値になります(終了はkの符号に依存します)。kをゼロにすることはできないことに注意してください。kがNoneの場合、1のように扱われます。
これは、スライスストライドが正の場合、省略されたスライスの開始がシーケンスの開始であり、省略されたスライスの終了がシーケンスの終了であることを意味します。スライスストライドが負の場合、それは反対です。次の2つの値のいずれかを入力すると、これを確認できます。
>>> '0123456'[:3]
'012'
>>> '0123456'[:3:-1]
'654'
>>> '0123456'[3:]
'3456'
>>> '0123456'[3::-1]
'3210'
これを考える1つの方法は、シーケンスをループとして視覚化することです。ここで、開始と終了は同じポイントです。スライスの一方の端を省略すると、この「両端点」を端点として使用するように指定するだけで、そこからどちらの方向に進むかは指定しません。どちらに進むかを示すのはストライドサインであり、これにより、「両端点」をシーケンスの開始または終了として扱うかどうかが決まります。
拡張スライスコンポーネントはすべてデフォルトでNoneに設定されています(0やsys.maxint
単純なスライスではありません)。
>>> class A:
... def __getitem__(self, s):
... return s
...
>>> A()[::-1]
slice(None, None, -1)
>>> A()[:]
slice(0, 9223372036854775807, None)
したがって、スライスがデフォルトでゼロから開始する必要があるという自動推定はありません。
これを視覚化する
スライスがどのように機能するかを覚える最良の方法は、インデックスを文字間を指すものと考えることです。最初の文字の左端には0の番号が付けられます。次に、n文字の文字列の最後の文字の右端にインデックスnが付けられます。 :
+---+---+---+---+---+
| H | e | l | l | o |
+---+---+---+---+---+
0 1 2 3 4 5
-5 -4 -3 -2 -1
インデックスは、右からカウントを開始するために負の数にすることができます。 ただし、-0は実際には0と同じであるため、右からはカウントされないことに注意してください。
In [105]: "helloworld"[-0]
Out[105]: 'h'
In [106]: "helloworld"[0]
Out[106]: 'h'
つまり、逆インデックスが-1から始まる理由
In [107]: "helloworld"[-1]
Out[107]: 'd'
文字列の最後から2番目のインデックスを取得する場合、[-2]
つまり、最後の1文字の負のステップが必要な場合は、次のインデックスに移動するためにステップが追加されます。
In [108]: "helloworld"[-1 + -1]
Out[108]: 'l'
ステップ=1の場合、当然のことながら、元の文字列を取得します。
step = -1の場合、Pythonはおそらく特殊なケースを実装します。順序を逆にします。
結局のところ、スライスは、意図された動作である完全なスライス[start:end]
を返します。[:]
したがって、これを2段階の操作と見なします。スライスを取得し(この場合は完全なコピー)、ステッピングを適用します(この場合は逆にします)。
Pythonでは、文字列インデックスは次のようになります。
"H e l l o"
0 1 2 3 4
-4 -3 -2 -1 0
使用されるインデックスは、取得するスライスの方向によって異なります。あなたが与えたステップは反対方向であるため、それは以下のインデックスを使用します。ただし、これはドキュメントでは明確にされていません。
編集:
私は実際に再確認し、興味深いことに
str[::-1]
str[0::-1]
str[-1::-1]
すべて同じ値を返します。ですから、元の投稿で私が言ったことは間違っているようです。これは、言語でのバグまたは特殊なケースの処理のように見えます。
あなたが見ているものは呼ばれていますstriding
:
>>> 'helloworld'[::1]
すべての要素を返します
>>> 'helloworld'[::2]
'hlool'
2番目ごとの要素を返します。だから、今試してみてください:
>>> 'helloworld'[::-2]
'drwle'
これにより、最後から2要素ごとに返されます。したがって、当然、最後からすべての要素は逆の文字列です。
>>> 'helloworld'[::-1]
'dlrowolleh'
-1
文字列が逆になっているときにstartが暗黙的でない場合は意味がありません。-1
明示的なインデックスを使用しようとすると、手順として使用するときに開始インデックスが終了インデックスの右側にある必要があることがわかります。
>>> "helloworld"[0:-1:-1]
''
>>> "helloworld"[-1:0:-1]
'dlrowolle'
通常の方法でスライスする場合と同様に、範囲には始点が含まれ、終点は含まれないためh
、インデックス0
では範囲の一部ではありません。(AFAIK)これは機能しないため、文字列全体を明示的に逆にすることはできないというスライス表記の制限です。
>>> "helloworld"[-1:-1:-1]
''
したがって、スライスアンドリバース関数には、このための特別なケースが必要になります。
def slice_and_reverse(s, a, b):
"Return a slice of s from a to but not including b, reversed."
if a == 0:
return s[b - 1::-1]
else:
return s[b - 1:a - 1:-1]