22

From the python documentation docs.python.org/tutorial/introduction.html#strings:

Slice indices have useful defaults; an omitted first index defaults to zero, an omitted second index defaults to the size of the string being sliced.

For the standard case, this makes a lot of sense:

>>> s = 'mystring'
>>> s[1:]
'ystring'
>>> s[:3]
'mys'
>>> s[:-2]
'mystri'
>>> s[-1:]
'g'
>>> 

So far, so good. However, using a negative step value seems to suggest slightly different defaults:

>>> s[:3:-1]
'gnir'
>>> s[0:3:-1]
''
>>> s[2::-1]
'sym'

Fine, perhaps if the step is negative, the defaults reverse. An ommitted first index defaults to the size of the string being sliced, an omitted second index defaults to zero:

>>> s[len(s):3:-1]
'gnir'

Looking good!

>>> s[2:0:-1]
'sy'

Whoops. Missed that 'm'.

Then there is everyone's favorite string reverse statement. And sweet it is:

>>> s[::-1]
'gnirtsym'

However:

>>> s[len(s):0:-1]
'gnirtsy'

The slice never includes the value of the second index in the slice. I can see the consistency of doing it that way.

So I think I am beginning to understand the behavior of slice in its various permutations. However, I get the feeling that the second index is somewhat special, and that the default value of the second index for a negative step can not actually be defined in terms of a number.

Can anyone concisely define the default slice indices that can account for the provided examples? Documentation would be a huge plus.

martineau
  • 119,623
  • 25
  • 170
  • 301
MikeG
  • 4,015
  • 2
  • 27
  • 25
  • As gnibbler points out below, sometimes you _do_ have to know how it just works. For example, if you have a function that takes three optional parameters and slices based on the values of those parameters, what should it use if the parameters are omitted? – abarnert Sep 21 '12 at 00:45
  • 1
    I knew I was being a bit pendantic, but sometimes I want to know exactly how things work. I don't trust magic :-). – MikeG Sep 22 '12 at 17:53
  • related to: https://stackoverflow.com/questions/509211/understanding-slicing – Izana Sep 17 '22 at 19:13

8 Answers8

21

There actually aren't any defaults; omitted values are treated specially.

However, in every case, omitted values happen to be treated in exactly the same way as None. This means that, unless you're hacking the interpreter (or using the parser, ast, etc. modules), you can just pretend that the defaults are None (as recursive's answer says), and you'll always get the right answers.

The informal documentation cited isn't quite accurate—which is reasonable for something that's meant to be part of a tutorial. For the real answers, you have to turn to the reference documentation.

For 2.7.3, Sequence Types describes slicing in notes 3, 4, and 5.

For [i:j]:

… If i is omitted or None, use 0. If j is omitted or None, use len(s).

And for [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.

For 3.3, Sequence Types has the exact same wording as 2.7.3.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 1
    The big key is to know when to check whether you're looking at the reference docs or not. Unfortunately, the "informal introduction" often isn't very informal and reads like reference documentation—and at the same time, fortunately, the reference documentation is much clearer than, say, the ISO C++ spec—so I often find myself reading the informal docs and wondering about something that's not fully specified, until I see that "tutorial" in the URL and figure out the problem… – abarnert Sep 24 '12 at 17:16
  • 1
    What are these end values of `i` and `j` in `[i:j:k]` for a positive k and a negative k? – Asad Moosvi Aug 14 '17 at 19:50
7

The end value is always exclusive, thus the 0 end value means include index 1 but not 0. Use None instead (since negative numbers have a different meaning):

>>> s[len(s)-1:None:-1]
'gnirtsym'

Note the start value as well; the last character index is at len(s) - 1; you may as well spell that as -1 (as negative numbers are interpreted relative to the length):

>>> s[-1:None:-1]
'gnirtsym'
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I am really confused about the upper bound when a negative step is used. What does an upper bound of `None` mean when the step is say `-1`? `s[-1:-len(s)-1:-1]` also returns a reversed string. So is the upper bound of `None` replaced by `-len(s)-1`? What I'm trying to understand is... does the upper bound of `None` get replaced by a certain integer index when a negative step is used? – Asad Moosvi Aug 15 '17 at 12:45
  • @AsadMoosvi: for the start and stop positions, the defaults are the start and end of sequence *in iteration direction*. So for negative steps, start is the end of the sequence, and stop is past `0` (but you can't specify `-1` because negative values are subtracted from the length before use, which is why you need to use `-len(s)-1` to make that work; for the specific value in my answer that's `-8 - 1` == `-9` and that is then used relative to length `8` so you end up with `-1` as the stop value in the end. Way more cumbersome than just using `None`). – Martijn Pieters Aug 15 '17 at 12:48
  • But `-len(s)-1` is a negative value in itself. Shouldn't it get subtracted from the length when it's used? – Asad Moosvi Aug 15 '17 at 12:51
  • 1
    @AsadMoosvi: yes, so you end up with `-1` *after* subtraction. Don't confuse the value you pass in with the end value the internal iteration ends up using. – Martijn Pieters Aug 15 '17 at 12:51
  • So ultimately the internal iteration ends up using `-1` as the value for upper bound, right? – Asad Moosvi Aug 15 '17 at 12:58
  • @AsadMoosvi: exactly. Using `None` or omitting the value altogether is justs a way of spelling *use -1 as the end value if the stride is negative*. – Martijn Pieters Aug 15 '17 at 12:59
4

The notes in the reference documentation for sequence types explains this in some detail:

(5.) 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). If i or j is greater than len(s), use len(s). 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.

So you can get the following behaviour:

>>> s = "mystring"
>>> s[2:None:-1]
'sym'
Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • In this case, what does the `None` mean? When used as the upper bound in a negative step? – Asad Moosvi Aug 15 '17 at 12:33
  • Is there a specific integer index that's substituted for None as the _upper bound_? `None`, when used for the lower bound gets substituted for `len(s) - 1` but what upper bound is used when it's `None`, specifically for a negative step? – Asad Moosvi Aug 15 '17 at 12:34
  • @AsadMoosvi The value of `None` is a special value that does not have an equivalent integer value. – Greg Hewgill Aug 15 '17 at 18:52
4

I don't have any documentation, but I think the default is [None:None:None]

>>> "asdf"[None:None:None]
'asdf'
>>> "asdf"[None:None:-1]
'fdsa'
recursive
  • 83,943
  • 34
  • 151
  • 241
  • 1
    This is useful to know if you need to create `slice()` objects, as you can't just say `slice(, , -1)`. It needs to be `slice(None, None, -1)` – John La Rooy Sep 20 '12 at 22:38
  • Actually, the default values are not None, but omitted values are always treated the same as None. So, if you're hacking the interpreter or using the parser/ast/compiler modules this is wrong, but for any other purpose it's right. – abarnert Sep 21 '12 at 00:43
  • @abarnert ..but at least when `__getitem__` is invoked by slicing the omitted fields have the value `None`. – minmaxavg May 04 '16 at 13:06
1

Actually it is logical ...

if you look to the end value, it always points to the index after the last index. So, using 0 as the end value, means it gets till element at index 1. So, you need to omit that value .. so that it returns the string you want.

>>> s = '0123456789'
>>> s[0], s[:0]
('0', '')
>>> s[1], s[:1]
('1', '0')
>>> s[2], s[:2]
('2', '01')
>>> s[3], s[:3]
('3', '012')
>>> s[0], s[:0:-1]
('0', '987654321')
Mahmoud Aladdin
  • 536
  • 1
  • 3
  • 13
1

Useful to know if you are implementing __getslice__: j defaults to sys.maxsize (https://docs.python.org/2/reference/datamodel.html#object.getslice)

>>> class x(str):
...   def __getslice__(self, i, j):
...     print i
...     print j
...
...   def __getitem__(self, key):
...     print repr(key)
...
>>> x()[:]
0
9223372036854775807
>>> x()[::]
slice(None, None, None)
>>> x()[::1]
slice(None, None, 1)
>>> x()[:1:]
slice(None, 1, None)
>>> import sys
>>> sys.maxsize
9223372036854775807L
Kevin Smyth
  • 1,892
  • 21
  • 22
0

There are excellent answers and the best one is selected as accepted answer, but if you are looking for a way to wrap your head around default values for slice, then it helps to imagine list as having two ends. Starting with HEAD end then the first element and so on, until the TAIL end after the last element.

Now answering the actual question:

There are two defaults for the slices

  1. Defaults when step is +ve

    0:TAIL:+ve step

  2. Defaults when step is -ve

    HEAD:-1:-ve step

Ajeet Ganga
  • 8,353
  • 10
  • 56
  • 79
0

Great question. I thought I knew how slicing worked until I read this post. While your question title asks about "default slice indices" and that's been answered by abarnet, Martijn, and others, the body of your post suggests your real question is "How does slicing work". So, I'll take a stab at that..

Explanation

Given your example, s = “mystring”, you can imagine a set of positive and negative indices.

 m  y  s  t  r  i  n  g
 0  1  2  3  4  5  6  7 <- positive indices
-8 -7 -6 -5 -4 -3 -2 -1 <- negative indices

We select slices of the form s[i:j:k]. The logic changes depending on whether k is positive or negative. I would describe the algorithm as follows.

if k is empty, set k = 1

if k is positive:
  move right, from i (inclusive) to j (exclusive) stepping by abs(k)
  if i is empty, start from the left edge
  if j is empty, go til the right edge

if k is negative:
  move left, from i (inclusive) to j (exclusive) stepping by abs(k)
  if i is empty, start from the right edge
  if j is empty, go til the left edge

(Note this isn't exactly pseudo code, as I intended it to be more comprehendible.)


Examples

>>> s[:3:]
'mys'

Here, k is empty so we set it equal to 1. Then since k is positive, we move right from i to j. Since i is empty, we start from the left edge and select everything up to but excluding the element at index 3.

>>> s[:3:-1]
'gnir'

Here, k is negative, so we move left from i to j. Since i is empty, we start from the right edge and select everything up to but excluding the element at index 3.

>>> s[0:3:-1]
''

Here, k is negative, so we move left from i to j. Since index 3 isn't to the left of index 0, no elements are selected and we get back the empty string.

Ben
  • 20,038
  • 30
  • 112
  • 189
  • I don't think this answers the question of default values and how s[len(s):0:-1] does not result in printing 'mystring'. – MattSt Jul 05 '22 at 07:07