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?
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.
Related
I am trying to understand slicing. According to Python official docs:
If i is omitted or None, use 0. If j is omitted or None, use len(s)
When k is positive, the above statement holds true. For example:
>>> s = 'Kaju Katli'
>>> s[:] == s[0:len(s)] #value of k is 1
True
When k is negative, and I put i as len(s) and j as 0, the element at the 0th index is not included. I know that the last element is always excluded that's why I am getting these results. For example:
>>> s = 'Kaju Katli'
>>> s[::-1] == s[len(s):0:-1]
False
So, my question is what should be the value of j in s[len(s):j:-1] to get 'iltaK ujaK' as the result, if s = 'Kaju katli'? Is None the only possible value of j in such cases?
s = "Kaju Katli"
j = -(len(s)+1) # or -len(s)-1 or ~len(s)
print(s[len(s):j:-1])
OUTPUT
iltaK ujaK
Why doesn't 'example'[999:9999] result in error? Since 'example'[9] does, what is the motivation behind it?
From this behavior I can assume that 'example'[3] is, essentially/internally, not the same as 'example'[3:4], even though both result in the same 'm' string.
You're correct! 'example'[3:4] and 'example'[3] are fundamentally different, and slicing outside the bounds of a sequence (at least for built-ins) doesn't cause an error.
It might be surprising at first, but it makes sense when you think about it. Indexing returns a single item, but slicing returns a subsequence of items. So when you try to index a nonexistent value, there's nothing to return. But when you slice a sequence outside of bounds, you can still return an empty sequence.
Part of what's confusing here is that strings behave a little differently from lists. Look what happens when you do the same thing to a list:
>>> [0, 1, 2, 3, 4, 5][3]
3
>>> [0, 1, 2, 3, 4, 5][3:4]
[3]
Here the difference is obvious. In the case of strings, the results appear to be identical because in Python, there's no such thing as an individual character outside of a string. A single character is just a 1-character string.
(For the exact semantics of slicing outside the range of a sequence, see mgilson's answer.)
For the sake of adding an answer that points to a robust section in the documentation:
Given a slice expression like 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
if you write s[999:9999], python is returning s[len(s):len(s)] since len(s) < 999 and your step is positive (1 -- the default).
Slicing is not bounds-checked by the built-in types. And although both of your examples appear to have the same result, they work differently; try them with a list instead.
What happens when we give string[::-1] in python such that the string gets reversed we know that by default the places are acquired by the 0 index and the last index+1 of the string or the -last index and -1 then how on writing above it starts counting from last index whether neither -1 is present at the first place nor the last index so that it starts decreasing 1 from where I can get depth knowledge of working of slicing in python and what does it return like stuff
From the python documentation on slicing sequences like [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.
https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
Actually in case of python string slicing it takes three arguments:
[firstArgument:secondArgument:thirdArgument]
FirstArgument consists in the integer value from where the string get sliced.
SecondArgument tells about the up to where the string being sliced.
ThirdArgument tells about that how much step it get jumped
Example:
a="Hello"
print(a[::2])
# outputs 'hlo' without quoting.
Why doesn't 'example'[999:9999] result in error? Since 'example'[9] does, what is the motivation behind it?
From this behavior I can assume that 'example'[3] is, essentially/internally, not the same as 'example'[3:4], even though both result in the same 'm' string.
You're correct! 'example'[3:4] and 'example'[3] are fundamentally different, and slicing outside the bounds of a sequence (at least for built-ins) doesn't cause an error.
It might be surprising at first, but it makes sense when you think about it. Indexing returns a single item, but slicing returns a subsequence of items. So when you try to index a nonexistent value, there's nothing to return. But when you slice a sequence outside of bounds, you can still return an empty sequence.
Part of what's confusing here is that strings behave a little differently from lists. Look what happens when you do the same thing to a list:
>>> [0, 1, 2, 3, 4, 5][3]
3
>>> [0, 1, 2, 3, 4, 5][3:4]
[3]
Here the difference is obvious. In the case of strings, the results appear to be identical because in Python, there's no such thing as an individual character outside of a string. A single character is just a 1-character string.
(For the exact semantics of slicing outside the range of a sequence, see mgilson's answer.)
For the sake of adding an answer that points to a robust section in the documentation:
Given a slice expression like 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
if you write s[999:9999], python is returning s[len(s):len(s)] since len(s) < 999 and your step is positive (1 -- the default).
Slicing is not bounds-checked by the built-in types. And although both of your examples appear to have the same result, they work differently; try them with a list instead.
Not sure where I picked this up, but it stuck and I use it all the time.
Can someone explain how this string reversal works? I use it to test for palindromic strings without converting it to a mutable type first.
>>> word = "magic"
>>> magic = word[::-1]
>>> magic
'cigam'
I would put my best guess, but I don't want to walk in with any preconceptions about the internals behind this useful trick.
The slice notation goes like this:
my_list[start:end:step]
So, when you do [::-1], it means:
start: nothing (default)
end: nothing (default)
step: -1 (descendent order)
So, you're going from the end of the list (default) to the first element (default), decreasing the index by one (-1).
So, as many answers said, there is no sorting nor in-place swapping, just slice notation.
You can have a look here - it is an extended slice.
"What's New in Python 2.3", section 15, "Extended Slices".
This "trick" is just a particular instance of applying a slice operation to a sequence. You can use it to produce a reversed copy of a list or a tuple as well. Another "trick" from the same family: [:] is often used to produce a (shallow) copy of a list.
"What's new in Python 2.3" is an unexpected entry point into the maze. Let's start at a more obvious(?) place, the current 2.X documentation for sequence objects.
In the table of sequence operations, you'll see a row with Operation = s[i:j:k], Result = "slice of s from i to j with step k", and Notes = "(3)(5)".
Note 3 says "If i or j is negative, the index is relative to the end of the string: len(s) + i or len(s) + j is substituted. But note that -0 is still 0."
Note 5 says "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."
We have k == -1, so the indices used are i, i-1, i-2, i-3 and so on, stopping when j is reached (but never including j). To obtain the observed effect, the "end" value used for i must be len(s)-1, and the "end" value used for j must be -1. Thus the indices used are last, last-1, ..., 2, 1.
Another entry point is to consider how we might produce such a result for any sequence if [::-1] didn't exist in the language:
def reverse_traversal_of_sequence(s):
for x in range(len(s) - 1, -1, -1):
do_something_with(s[x])