Python string reverse

Reverse a string in Python

up vote 667 down vote favorite
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. 

share improve this question
 
3  
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 

14 Answers

active oldest votes
up vote 1381 down vote accepted

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.

share improve this answer
 
85  
That's very pythonic. Good job! –  dionyziz  Feb 22 '12 at 19:23
59  
@PaoloBergantino You, sir, are a gentleman and a scholar. –  TigOldBitties  May 19 '12 at 15:43
89  
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
46  
@odigity: Perhaps until you understand Python you should reserve your judgment. –  Paolo Bergantino  Feb 4 '13 at 20:37
52  
Do I need to be a Python expert to have an opinion about the readability of code? –  odigity  Feb 4 '13 at 22:10
up vote 145 down vote

@Paolo's s[::-1] is fastest; a slower approach (maybe more readable, but that's debatable) is ''.join(reversed(s)).

share improve this answer
 
76  
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
1  
Looks like about an order of magnitude faster. –  Brian Peterson  Sep 22 '13 at 2:00
11  
I prefer the "reversed()" solution. Readability counts. –  guettli  Dec 6 '13 at 13:21
2  
@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 
1  
@buzhidao, because reversed returns an instance of not a string! –  Alex Martelli  May 5 '15 at 23:23
up vote 19 down 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
share improve this answer
 
2  
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 
1  
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
1  
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
up vote 10 down 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

Background

This 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()?

Problem

  • Context
    • Python 2.x
    • Python 3.x
  • Scenario:
    • Developer wants to transform a string
    • Transformation is to reverse order of all the characters

Solution

  • example01 produces the desired result, using extended slice notation. 

Pitfalls

  • Developer 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

Rationale

Python 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'

Conclusion

The 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 also

  • alternate simple approach
  • alternate simple approach
  • alternate explanation of slice notation

你可能感兴趣的:(Python,String)