Reverse
a string in Python
up vote667down votefavorite 191
| There is no built in reverse function in Python's str object. What is the best way of implementing this?
If supplying a very concise answer, please elaborate on it's efficiency. Is the str converted to a different object, etc.
python string
|
| | Not the best way by far, but one of the many ways to do it if you weren't allowed to use slicing or reversed(), is ''.join((s[i]
for i in xrange(len(s)-1, -1, -1))) . – Dennis Mar 6 '13 at 6:49
| add a comment
|
14 Answers
activeoldestvotesup vote1381down voteaccepted
| How about:
>>> 'hello world'[::-1]
'dlrow olleh'
This is extended slice syntax. It works by doing [begin:end:step] - by leaving begin and end off and specifying a step of -1, it reverses a string.
|
| | That's very pythonic. Good job! – dionyziz Feb 22 '12 at 19:23
| | @PaoloBergantino You, sir, are a gentleman and a scholar. – TigOldBitties May 19 '12 at 15:43
| | Wow. I was horrified at first by the solution Paolo proposed, but that took a back seat to the horror I felt upon reading the first comment: "That's very pythonic. Good job!" I'm so disturbed that such a bright community thinks using such cryptic methods for something so basic is a good idea. Why isn't it just s.reverse()? – odigity Feb 4 '13 at 19:49
| | @odigity: Perhaps until you understand Python you should reserve your judgment. – Paolo Bergantino Feb 4 '13 at 20:37
| | Do I need to be a Python expert to have an opinion about the readability of code? – odigity Feb 4 '13 at 22:10
| show 19 more comments
|
up vote145down vote
| @Paolo's s[::-1] is fastest; a slower approach (maybe more readable, but that's debatable) is ''.join(reversed(s)) .
|
| | A lot faster: $ python -m timeit s='s = "abcdef"' 'mystr = mystr[::-1]' 1000000 loops, best of 3: 0.263 usec per loop $ python -m timeit s='s = "abcdef"' 'mystr = "".join(reversed(mystr))' 100000 loops, best of 3: 2.29 usec per loop – telliott99 Jan 23 '10 at 17:55
| | Looks like about an order of magnitude faster. – Brian Peterson Sep 22 '13 at 2:00
| | I prefer the "reversed()" solution. Readability counts. – guettli Dec 6 '13 at 13:21
| | @smci yes, his syntax is incorrect, the correct one is: python
-mtimeit 'mystr="abcdef"' 'mystr = mystr[::-1]' which yields 10000000 loops, best of 3: 0.142 usec per loop and python
-mtimeit 'mystr="abcdef"' 'mystr = "".join(reversed(mystr))' which yields 1000000 loops, best of 3: 0.669 usec per loop, on Python 3.4 , on a GNU/Linux system with Linux kernel 3.13.0-40-generic x86_64 and Intel Core 2 Duo CPU, T9300 @ 2.50GHz CPU – Mnemonic Flow Dec 8 '14 at 22:10
| | @buzhidao, because reversed returns an instance of <type
'reversed'> , not a string! – Alex Martelli May 5 '15 at 23:23
| show 2 more comments
|
up vote19down vote
| Attempting a canonical answer for this question:
"There is no built in reverse function in Python's str object. What is the best way of implementing this?"
While ''.join(reversed('foo')) is readable, it requires calling a string method, str.join , on another called function, which can be rather slow
Much faster is using a reverse slice:
'foo'[::-1]
But how can we make this more readable and understandable to someone less familiar with the intent of the original author? Let's create a named slice object, and pass it to the subscript notation.
start = stop = None
step = -1
reverse_slice = slice(start, stop, step)
'foo'[reverse_slice]
Implement as Function
To actually implement this as a function, I think it is semantically clear enough to simply use a descriptive name:
def reversed_string(a_string):
return a_string[::-1]
And usage is simply:
reversed_string('foo')
Demo of timings (differences are probably exacerbated by the shortness of the string being reversed):
>>> min(timeit.repeat("''.join(reversed('foo'))"))
2.2613844704083021
>>> min(timeit.repeat("'foo'[::-1]"))
0.28049658041891234
>>> min(timeit.repeat("start=stop=None; step=-1; 'foo'[start:stop:step]"))
0.37622163503510819
>>> min(timeit.repeat("start=stop=None; step=-1; reverse_slice = slice(start, stop, step); 'foo'[reverse_slice]"))
0.54768217598029878
And for the function:
>>> def reversed_string(a_string):
... return a_string[::-1]
...
>>> min(timeit.repeat("reversed_string('foo')", 'from __main__ import reversed_string'))
0.47710669976368081
shareimprove this answer
| edited Aug 13 '15 at 1:05
| answered Jan 8 '15 at 15:32
Aaron Hall 40.8k16119121
|
|
| | These are bad benchmarks. You shouldn't time the creation of the slice object. So this leads to really interesting results: min(timeit.repeat("'foo'[reverse_slice]",
setup="reverse_slice = slice(None, None, -1)")) is actually faster than 'foo'[::-1] . And a reverse_string function using a global defined reverse_slice is almost as fast as 'foo'[::-1] . – schlamar Jan 28 '15 at 10:20
| | Most Python programmers prefer to use as few globals as possible. I can't imagine anyone using a global slice object instead of the alternatives, and since a slice object is created by slice notation (see the CPython source hg.python.org/cpython/file/2.7/Objects/stringobject.c#l1307) inside the subscript, it does seem fair to compare it within the function. Yes the slice object creation is much slower with the callable constructor, but I'm comparing apples to apples with realistic code. – Aaron Hall Jan 28 '15 at 13:28
| | From a naming perspective, "reversed_string" may be clearer since it doesn't imply mutation. It sounds like "give me the reversed version of this string" as opposed to "please reverse this string." Much like "sorted" (which returns a sorted list) or random.shuffle (which shuffles the list in place) – iAdjunct Jun 26 '15 at 4:38
| | @iAdjunct that's a very good suggestion, I've taken your advice. – Aaron Hall Aug 13 '15 at 1:06
| add a comment
|
up vote10down vote
| Quick Answer (TL;DR)
Example
### example01 -------------------
mystring = 'coup_ate_grouping'
backwards = mystring[::-1]
print backwards
### ... or even ...
mystring = 'coup_ate_grouping'[::-1]
print mystring
### result01 -------------------
'''
gnipuorg_eta_puoc
'''
Detailed Answer
BackgroundThis answer is provided to address the following concern from a user odigity:
Wow. I was horrified at first by the solution Paolo proposed, but that took a back seat to the horror I felt upon reading the first comment: "That's very pythonic. Good job!" I'm so disturbed that such a bright community thinks using such cryptic methods for something so basic is a good idea. Why isn't it just s.reverse()?
ProblemContext
Python 2.x Python 3.x
Scenario:
Developer wants to transform a string Transformation is to reverse order of all the characters
Solutionexample01 produces the desired result, using extended slice notation.
PitfallsDeveloper might expect something like string.reverse() The native idiomatic (aka "pythonic") solution may not be readable to newer developers Developer may be tempted to implement his or her own version of string.reverse() to avoid slice notation. The output of slice notation may be counter-intuitive in some cases:
see e.g., example02
print
'coup_ate_grouping'[-4:] ## => 'ping' compared to
print
'coup_ate_grouping'[-4:-1] ## => 'pin' compared to
print
'coup_ate_grouping'[-1] ## => 'g'
the different outcomes of indexing on [-1] may throw some developers off
RationalePython has a special circumstance to be aware of: a string is an iterable type.
One rationale for excluding a string.reverse() method is to give python developers incentive to leverage the power of this special circumstance.
In simplified terms, this simply means each individual character in a string can be easily operated on as a part of a sequential array of elements, just like arrays in other programming languages.
To understand how this works, reviewing example02 can provide a good overview.
Example02
### example02 -------------------
## start (with positive integers)
print 'coup_ate_grouping'[0] ## => 'c'
print 'coup_ate_grouping'[1] ## => 'o'
print 'coup_ate_grouping'[2] ## => 'u'
## start (with negative integers)
print 'coup_ate_grouping'[-1] ## => 'g'
print 'coup_ate_grouping'[-2] ## => 'n'
print 'coup_ate_grouping'[-3] ## => 'i'
## start:end
print 'coup_ate_grouping'[0:4] ## => 'coup'
print 'coup_ate_grouping'[4:8] ## => '_ate'
print 'coup_ate_grouping'[8:12] ## => '_gro'
## start:end
print 'coup_ate_grouping'[-4:] ## => 'ping' (counter-intuitive)
print 'coup_ate_grouping'[-4:-1] ## => 'pin'
print 'coup_ate_grouping'[-4:-2] ## => 'pi'
print 'coup_ate_grouping'[-4:-3] ## => 'p'
print 'coup_ate_grouping'[-4:-4] ## => ''
print 'coup_ate_grouping'[0:-1] ## => 'coup_ate_groupin'
print 'coup_ate_grouping'[0:] ## => 'coup_ate_grouping' (counter-intuitive)
## start:end:step (or stop:end:stride)
print 'coup_ate_grouping'[-1::1] ## => 'g'
print 'coup_ate_grouping'[-1::-1] ## => 'gnipuorg_eta_puoc'
## combinations
print 'coup_ate_grouping'[-1::-1][-4:] ## => 'gnipuorg_eta_puoc'
ConclusionThe cognitive load associated with understanding how slice notation works in python may indeed be too much for some adopters and developers who do not wish to invest much time in learning the language.
Nevertheless, once the basic principles are understood, the power of this approach over fixed string manipulation methods can be quite favorable.
For those who think otherwise, there are alternate approaches, such as lambda functions, iterators, or simple one-off function declarations.
If desired, a developer can implement her own string.reverse() method, however it is good to understand the rationale behind this "quirk" of python.
See alsoalternate simple approach alternate simple approach alternate explanation of slice notation
|
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理