Python string reverse

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

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

@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 <type 'reversed'>not a string! –  Alex Martelli  May 5 '15 at 23:23

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

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

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值