-2

I have had a look at the top answers on why-does-list-1-not-equal-listlenlist-1 and what-are-the-default-slice-indices-in-python-really to learn about what the default values are set to in a slice. Both of the top answers refer to the following documentation:

Given s[i:j:k],

If i or j are omitted or None, they become “end” values (which end depends on the sign of k). Note, k cannot be zero. If k is None, it is treated like 1.

Suppose I have the following code:

s = "hello"
forward_s = s[::1]
print(forward_s) # => "hello"

backward_s = s[::-1]
print(backward_s) # => "olleh"

I know that if the indices are omitted then Python treats that as if the value of None was used in those places. According to the documentation the indices of i and j in [i:j:k] are set to "end" values depending on the sign of k. For a positive k, I'm assuming i is set to 0 and j is set to the length of the string. What are the values set for a negative k though?

For instance, the following also reverses the string:

reversed_str = s[-1:-6:-1]

So maybe the default value for i is set to -1 and j is set to -len(s) - 1 when k = -1?

Asad Moosvi
  • 487
  • 5
  • 18
  • that seems like the correct analysis. otherwise it would try to go from 0 to -1 to -2... so `[::-1]` would not properly reverse the list, but rather would give the first element and then the rest reversed. – Jason Stein Aug 14 '17 at 20:54
  • What are "end values"? – Asad Moosvi Aug 14 '17 at 20:55
  • When k is negative, i and j are treated as end values from the terminating end of the string. The docs, quoted in the answer you linked are explicit about that. – pvg Aug 14 '17 at 20:55
  • Okay well if `len(str) - 1` and `0` are the "end values" then why doesn't this work: `"hello"[len("hello") - 1: 0: -1]`? This returns "olle" instead of "olleh". – Asad Moosvi Aug 14 '17 at 20:59
  • That's what I want to clarify. Whether `-1` and `-len(s) - 1` are the other set of end values, specifically for a negative value of k. – Asad Moosvi Aug 14 '17 at 21:01

1 Answers1

1

The default values are None, None, and None.

class Sliceable(object):
    def __getitem__(self, slice):
        print(slice)

Sliceable()[::]
>>> slice(None, None, None)

This is regardless of the fact, that slice() does require a stop argument. The library documentation is a little bit less explicit on this, but the C API makes clear, that all three values maybe empty:

The start, stop, and step parameters are used as the values of the slice object attributes of the same names. Any of the values may be NULL, in which case the None will be used for the corresponding attribute.

It's up to the sliceable object to make sense of the default values. The convention is to use first element, past last element, minimal stepping as implemented by the builtin collections:

l = [1, 2, 3]
l[slice(None, None, None])
>>> [1, 2, 3]
s[None:None:None])
>>> [1, 2, 3]

Negative stepping will cause the default start and end values to be reversed semantically, i.e.:

s = 'abc'
s[slice(None, None, -1)]
>>> 'cba'
s[::-1]
>>> 'cba'

Note that this does not mean a simple value flip, the default value of end is typically "one past the end of the sequence, in whatever direction", since range() is not inclusive for the end value, but the default values for a slice should include the full sequence.

This is documented here:

s[i:j:k]

The slice of s from i to j with step k is defined as the sequence of items with index x = i + n*k such that 0 <= n < (j-i)/k. In other words, the indices are i, i+k, i+2*k, i+3*k and so on, stopping when j is reached (but never including j). When k is positive, i and j are reduced to len(s) if they are greater. When k is negative, i and j are reduced to len(s) - 1 if they are greater. If i or j are omitted or None, they become “end” values (which end depends on the sign of k). Note, k cannot be zero. If k is None, it is treated like 1.

Community
  • 1
  • 1
dhke
  • 15,008
  • 2
  • 39
  • 56
  • What does `None` mean here: `"foo"[None:None:-1]`? So the first None I assume means the last character in the string. Now what does the second None translate to? – Asad Moosvi Aug 14 '17 at 21:06
  • Is the second `None` interpreted as `-len(s) - 1`? `s` being the string in question. – Asad Moosvi Aug 14 '17 at 21:07
  • @AsadMoosvi As noted: For a collection builtin `c`(and any object mimicking such), `None` as an `end` argument is interpreted as `len(s)`), The actual value passed to `__getitem__()` is still `None`. Note that slice ranges are end-exclusive. – dhke Aug 14 '17 at 21:13
  • But that doesn't make sense. If the `end` argument is `-1` then what is the `start` argument? For reversing a string? – Asad Moosvi Aug 14 '17 at 21:16
  • @AsadMoosvi Sorry, typo, it's `len(s)` (-1 is `len(s) - 1`) of course. And `start` is `None`, which is interpreted as `0`. – dhke Aug 14 '17 at 21:17
  • What are are the **explicit** values used for `start` and `end` when the `step` is negative? Specifically for *strings* – Asad Moosvi Aug 14 '17 at 21:17
  • The explicit values are `None`. Are you asking about the implicit values? – pvg Aug 14 '17 at 21:20
  • `end` is `len(s)` for a positive value of the step argument. I understand that. But what is it for a negative step? That's what I'm confused about. – Asad Moosvi Aug 14 '17 at 21:20
  • @AsadMoosvi Negative steppings cause `start` and `end` to flip. The gritty details are [here](https://github.com/python/cpython/blob/143be366295038b36fc32c44b8e1b48a375eab56/Objects/unicodeobject.c#L9239). – dhke Aug 14 '17 at 21:22
  • That doesn't make sense either because if they flipped then `"hello"[len("hello"):0:-1]` should be the same as `"hello"[::-1]`. Here `start` and `end` have "flipped" but the result is not the same. – Asad Moosvi Aug 14 '17 at 21:23
  • @AsadMoosvi Hrm, yeah. Read as "for the end value you always get one passed the end of the sequence, in whatever direction" – dhke Aug 14 '17 at 21:27
  • I mean I _intuitively_ understand what it does. Like I know what's happening here. But I want to know for certain what Python is using for the `end` argument as the default for a negative step. I just hate learning by just accepting something as that's how it "just works." – Asad Moosvi Aug 14 '17 at 21:30
  • @AsadMoosvi And why don't you just go down into the implementation and check? Because the explanation for your initial question literally stops at "**it's up to the sliceable object to make sense of the default values.**". – dhke Aug 14 '17 at 21:31
  • I would've if I'd known C. – Asad Moosvi Aug 14 '17 at 21:33
  • @AsadMoosvi The semantic explanation (added to the answer) isn't much better ;-) – dhke Aug 14 '17 at 21:38