- Python reverse-stride slicing
- 8 Answers 8
- Python Reverse String - 5 Ways and the Best One
- 1. How to Reverse a String in Python?
- 1.1) Python Reverse String using Slicing
- 1.2) Reverse String using For Loop
- 1.3) Reverse a String using While Loop
- 1.4) Reverse a String using join() and reversed()
- 1.5) Python Reverse String using List reverse()
- 1.6) Python Reverse String using Recursion
- 2. Best Way to Reverse a String in Python
- 3. Summary
- 4. References
Python reverse-stride slicing
It seems strange that I can write foo[4:0:-1], foo[5:1:-1], etc. and get what I would expect, but there’s no way to write the slice so that I get ‘3210’. A makeshift way of doing this would be foo[0:4][::-1], but this creates two string objects in the process. I will be performing this operation literally billions of times, so every string operation is expensive. I must be missing something silly and easy. Thanks for your help!
«but this creates two string objects in the process» not necessarily — internally Python is free to do whatever optimizations it can get away with without breaking code, and since strings are semantically immutable, it could do slice objects that present themselves as strings that point to the same memory as the original string. It could also be coded to notice that there are no other references to the first slice object, and reuse the first slice object as second by just modifying the values in it in-place. Does it actually? Probably not. But PyPy’s JIT might, and future Python could.
8 Answers 8
Simply exclude the end range index.
Ironically, about the only option I think you didn’t try.
The problem there is that this slice operation is being used in an algorithm that works somewhat like as follows: foo[i:i-4:-1], and starts with a high ‘i’ and walks down. I can’t just remove the ‘i’. I suppose I could use an edge case and say «if i-4 < 0, use this other slice".
I’ll set this as ‘accept’ if it’s the only way, but using the edge-case of removing the middle operand for the final ‘reverse substring’ (and not for any of the other reverse substrings) seems clunky.
well, to be fair you didn’t include that condition in your question and even then you can wrap that «clunky» logic in a function and be done with it.
@Alice -1 is always the last element of the list (or the space between it and the preceding element, depending on your view of list indices). I agree, when using a negative step it would be nice if -1 meant the space beyond the start of the list, like how len(L) means the space beyond the end of the list. If you use -1 as the stop and step -1, you’ll have an empty slice.
If you wanted to use negative indices, you would have to count the number of negative steps, something like s[len(s)-1 : — (len(s)+1) : -1]
Omit the end index in your slice notation:
If you have to do this many times, create a slice object that you can use over and over
>>> first_4_items_reversed = slice(3,None,-1) >>> foo[first_4_items_reversed] '3210'
If you’re looking for something a little more human-readable than extended slice notation:
>>> foo = '0123456' >>> ''.join(reversed(foo[0:4])) '3210'
Thanks, but no, this creates three memory objects. join and reversed are pretty fast, but I think it’s safe to assume that extended slice notation will be faster.
I generally run under the assumption that if you’re writing in python, efficiency isn’t the most important thing. The only other thing that could be important is readability, so I optimize for that. Extended slice notation, particularly with negative step values, comes across as very opaque.
@eblume ideas don’t have speed and don’t create memory objects — implementations do. If you’re worried about speed in Python, start by using PyPy, not micro-optimizing how you reverse things. And in the meantime, remember that any slice notation optimization you can conceive of in principle can be done in reversed — and since in Python it’s much easier to monkeypatch/shadow functions from the outside than operators, you could literally write your code to use reversed and then just in the namespace of hot loops do reversed = my_epic_optimized_c_reversed .
@eblume in any case it’s not safe to assume that, because a sufficiently advanced hypothetical optimizer would understand a construct like ».join(reversed(foo[x:y])) and would just generate code which just directly copies characters backwards from foo from index y-1 to index x directly into a new string’s backing memory allocated with knowledge that y-x was the needed size. And in the meantime, JIT-optimizing VMs like PyPy (and modern compilers) are getting impressively close to those kinds of optimizations — and the clearer the code, the more it naturally lends itself to optimizers.
After reading the «technical documentation» (here) — specifically the sentence:
If either bound is negative, the sequence’s length is added to it.
I decided to try this, and it worked:
>>> foo = '0123456' >>> foo[3:-1-len(foo):-1] '3210' >>>
So I think the best answer to programmatically determine the «end point» would be to provide a well named helper function that makes it clear that its arguments are always treated like positive offsets, maybe special_slice()
I think the clarity of this ‘special’ case is extremely important since lots of common and significant use cases depend on the default behavior of negative offsets (i.e. adding the length to them). Personally I frequently use a ‘-1’ end point to mean: stop just before last element.
. algorithm that works somewhat like as follows: foo[i:i-4:-1], and starts with a high ‘i’ and walks down.
I might make the following:
def slice_by_len(data, start, length, step=1): end = start + length if step > 0 else start - length if end < 0: # Fix the negative offset to get what we really want end -= len(data) return data[start:end:step]
And then call it for each slice required:
foo_part = slice_by_len(foo, i, 4, -1)
The above could easily go in a loop over values of 'i'
Python Reverse String - 5 Ways and the Best One
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
Python String doesn’t have a built-in reverse() function. However, there are various ways to reverse a string in Python.
1. How to Reverse a String in Python?
- Using Slicing to create a reverse copy of the string.
- Using for loop and appending characters in reverse order
- Using while loop to iterate string characters in reverse order and append them
- Using string join() function with reversed() iterator
- Creating a list from the string and then calling its reverse() function
- Using Recursion
1.1) Python Reverse String using Slicing
def reverse_slicing(s): return s[::-1] input_str = 'ABç∂EF' if __name__ == "__main__": print('Reverse String using slicing =', reverse_slicing(input_str))
If you run above Python script, the output will be:
Reverse String using slicing = FE∂çBA
1.2) Reverse String using For Loop
def reverse_for_loop(s): s1 = '' for c in s: s1 = c + s1 # appending chars in reverse order return s1 input_str = 'ABç∂EF' if __name__ == "__main__": print('Reverse String using for loop =', reverse_for_loop(input_str))
Output: Reverse String using for loop = FE∂çBA
1.3) Reverse a String using While Loop
def reverse_while_loop(s): s1 = '' length = len(s) - 1 while length >= 0: s1 = s1 + s[length] length = length - 1 return s1 input_str = 'ABç∂EF' if __name__ == "__main__": print('Reverse String using while loop =', reverse_while_loop(input_str))
1.4) Reverse a String using join() and reversed()
def reverse_join_reversed_iter(s): s1 = ''.join(reversed(s)) return s1
1.5) Python Reverse String using List reverse()
def reverse_list(s): temp_list = list(s) temp_list.reverse() return ''.join(temp_list)
1.6) Python Reverse String using Recursion
def reverse_recursion(s): if len(s) == 0: return s else: return reverse_recursion(s[1:]) + s[0]
2. Best Way to Reverse a String in Python
We can reverse a string through multiple algorithms. We have already seen six of them. But which of them you should choose to reverse a string. We can use timeit module to run multiple iterations of these functions and get the average time required to run them. All the above functions are stored in a python script named string_reverse.py . I executed all these functions one by one for 1,00,000 times using the timeit module and got the average of the best 5 runs.
$ python3.7 -m timeit --number 100000 --unit usec 'import string_reverse' 'string_reverse.reverse_slicing("ABç∂EF"*10)' 100000 loops, best of 5: 0.449 usec per loop $ python3.7 -m timeit --number 100000 --unit usec 'import string_reverse' 'string_reverse.reverse_list("ABç∂EF"*10)' 100000 loops, best of 5: 2.46 usec per loop $ python3.7 -m timeit --number 100000 --unit usec 'import string_reverse' 'string_reverse.reverse_join_reversed_iter("ABç∂EF"*10)' 100000 loops, best of 5: 2.49 usec per loop $ python3.7 -m timeit --number 100000 --unit usec 'import string_reverse' 'string_reverse.reverse_for_loop("ABç∂EF"*10)' 100000 loops, best of 5: 5.5 usec per loop $ python3.7 -m timeit --number 100000 --unit usec 'import string_reverse' 'string_reverse.reverse_while_loop("ABç∂EF"*10)' 100000 loops, best of 5: 9.4 usec per loop $ python3.7 -m timeit --number 100000 --unit usec 'import string_reverse' 'string_reverse.reverse_recursion("ABç∂EF"*10)' 100000 loops, best of 5: 24.3 usec per loop
The below table presents the results and slowness of an algorithm from the best one.
Algorithm | TimeIt Execution Time (Best of 5) | Slowness |
---|---|---|
Slicing | 0.449 usec | 1x |
List reverse() | 2.46 usec | 5.48x |
reversed() + join() | 2.49 usec | 5.55x |
for loop | 5.5 usec | 12.25x |
while loop | 9.4 usec | 20.94x |
Recursion | 24.3 usec | 54.12x |
3. Summary
We should use slicing to reverse a string in Python. Its code is very simple and small and we don’t need to write our own logic to reverse the string. Also, it’s the fastest way to reverse a string as identified by the above test executions.
You can checkout complete python script and more Python examples from our GitHub Repository.
4. References
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.