[python] Reverse a string in Python

There is no built in reverse function for Python's str object. What is the best way of implementing this method?

If supplying a very concise answer, please elaborate on its efficiency. For example, whether the str object is converted to a different object, etc.

This question is related to python string

The answer is


What is the best way of implementing a reverse function for strings?

My own experience with this question is academic. However, if you're a pro looking for the quick answer, use a slice that steps by -1:

>>> 'a string'[::-1]
'gnirts a'

or more readably (but slower due to the method name lookups and the fact that join forms a list when given an iterator), str.join:

>>> ''.join(reversed('a string'))
'gnirts a'

or for readability and reusability, put the slice in a function

def reversed_string(a_string):
    return a_string[::-1]

and then:

>>> reversed_string('a_string')
'gnirts_a'

Longer explanation

If you're interested in the academic exposition, please keep reading.

There is no built-in reverse function in Python's str object.

Here is a couple of things about Python's strings you should know:

  1. In Python, strings are immutable. Changing a string does not modify the string. It creates a new one.

  2. Strings are sliceable. Slicing a string gives you a new string from one point in the string, backwards or forwards, to another point, by given increments. They take slice notation or a slice object in a subscript:

    string[subscript]
    

The subscript creates a slice by including a colon within the braces:

    string[start:stop:step]

To create a slice outside of the braces, you'll need to create a slice object:

    slice_obj = slice(start, stop, step)
    string[slice_obj]

A readable approach:

While ''.join(reversed('foo')) is readable, it requires calling a string method, str.join, on another called function, which can be rather relatively slow. Let's put this in a function - we'll come back to it:

def reverse_string_readable_answer(string):
    return ''.join(reversed(string))

Most performant approach:

Much faster is using a reverse slice:

'foo'[::-1]

But how can we make this more readable and understandable to someone less familiar with slices or the intent of the original author? Let's create a slice object outside of the subscript notation, give it a descriptive name, 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')

What your teacher probably wants:

If you have an instructor, they probably want you to start with an empty string, and build up a new string from the old one. You can do this with pure syntax and literals using a while loop:

def reverse_a_string_slowly(a_string):
    new_string = ''
    index = len(a_string)
    while index:
        index -= 1                    # index = index - 1
        new_string += a_string[index] # new_string = new_string + character
    return new_string

This is theoretically bad because, remember, strings are immutable - so every time where it looks like you're appending a character onto your new_string, it's theoretically creating a new string every time! However, CPython knows how to optimize this in certain cases, of which this trivial case is one.

Best Practice

Theoretically better is to collect your substrings in a list, and join them later:

def reverse_a_string_more_slowly(a_string):
    new_strings = []
    index = len(a_string)
    while index:
        index -= 1                       
        new_strings.append(a_string[index])
    return ''.join(new_strings)

However, as we will see in the timings below for CPython, this actually takes longer, because CPython can optimize the string concatenation.

Timings

Here are the timings:

>>> a_string = 'amanaplanacanalpanama' * 10
>>> min(timeit.repeat(lambda: reverse_string_readable_answer(a_string)))
10.38789987564087
>>> min(timeit.repeat(lambda: reversed_string(a_string)))
0.6622700691223145
>>> min(timeit.repeat(lambda: reverse_a_string_slowly(a_string)))
25.756799936294556
>>> min(timeit.repeat(lambda: reverse_a_string_more_slowly(a_string)))
38.73570013046265

CPython optimizes string concatenation, whereas other implementations may not:

... do not rely on CPython's efficient implementation of in-place string concatenation for statements in the form a += b or a = a + b . This optimization is fragile even in CPython (it only works for some types) and isn't present at all in implementations that don't use refcounting. In performance sensitive parts of the library, the ''.join() form should be used instead. This will ensure that concatenation occurs in linear time across various implementations.


With python 3 you can reverse the string in-place meaning it won't get assigned to another variable. First you have to convert the string into a list and then leverage the reverse() function.

https://docs.python.org/3/tutorial/datastructures.html

   def main():
        my_string = ["h","e","l","l","o"]
        print(reverseString(my_string))

    def reverseString(s):
      print(s)
      s.reverse()
      return s

    if __name__ == "__main__":
        main()

def reverse(input):
    return reduce(lambda x,y : y+x, input)

Thats my way:

def reverse_string(string):
    character_list = []
    for char in string:
        character_list.append(char)
    reversed_string = ""
    for char in reversed(character_list):
        reversed_string += char
    return reversed_string

The existing answers are only correct if Unicode Modifiers / grapheme clusters are ignored. I'll deal with that later, but first have a look at the speed of some reversal algorithms:

enter image description here

list_comprehension  : min:   0.6µs, mean:   0.6µs, max:    2.2µs
reverse_func        : min:   1.9µs, mean:   2.0µs, max:    7.9µs
reverse_reduce      : min:   5.7µs, mean:   5.9µs, max:   10.2µs
reverse_loop        : min:   3.0µs, mean:   3.1µs, max:    6.8µs

enter image description here

list_comprehension  : min:   4.2µs, mean:   4.5µs, max:   31.7µs
reverse_func        : min:  75.4µs, mean:  76.6µs, max:  109.5µs
reverse_reduce      : min: 749.2µs, mean: 882.4µs, max: 2310.4µs
reverse_loop        : min: 469.7µs, mean: 577.2µs, max: 1227.6µs

You can see that the time for the list comprehension (reversed = string[::-1]) is in all cases by far the lowest (even after fixing my typo).

String Reversal

If you really want to reverse a string in the common sense, it is WAY more complicated. For example, take the following string (brown finger pointing left, yellow finger pointing up). Those are two graphemes, but 3 unicode code points. The additional one is a skin modifier.

example = ""

But if you reverse it with any of the given methods, you get brown finger pointing up, yellow finger pointing left. The reason for this is that the "brown" color modifier is still in the middle and gets applied to whatever is before it. So we have

  • U: finger pointing up
  • M: brown modifier
  • L: finger pointing left

and

original: LMU
reversed: UML (above solutions)
reversed: ULM (correct reversal)

Unicode Grapheme Clusters are a bit more complicated than just modifier code points. Luckily, there is a library for handling graphemes:

>>> import grapheme
>>> g = grapheme.graphemes("")
>>> list(g)
['', '']

and hence the correct answer would be

def reverse_graphemes(string):
    g = list(grapheme.graphemes(string))
    return ''.join(g[::-1])

which also is by far the slowest:

list_comprehension  : min:    0.5µs, mean:    0.5µs, max:    2.1µs
reverse_func        : min:   68.9µs, mean:   70.3µs, max:  111.4µs
reverse_reduce      : min:  742.7µs, mean:  810.1µs, max: 1821.9µs
reverse_loop        : min:  513.7µs, mean:  552.6µs, max: 1125.8µs
reverse_graphemes   : min: 3882.4µs, mean: 4130.9µs, max: 6416.2µs

The Code

#!/usr/bin/env python

import numpy as np
import random
import timeit
from functools import reduce
random.seed(0)


def main():
    longstring = ''.join(random.choices("ABCDEFGHIJKLM", k=2000))
    functions = [(list_comprehension, 'list_comprehension', longstring),
                 (reverse_func, 'reverse_func', longstring),
                 (reverse_reduce, 'reverse_reduce', longstring),
                 (reverse_loop, 'reverse_loop', longstring)
                 ]
    duration_list = {}
    for func, name, params in functions:
        durations = timeit.repeat(lambda: func(params), repeat=100, number=3)
        duration_list[name] = list(np.array(durations) * 1000)
        print('{func:<20}: '
              'min: {min:5.1f}µs, mean: {mean:5.1f}µs, max: {max:6.1f}µs'
              .format(func=name,
                      min=min(durations) * 10**6,
                      mean=np.mean(durations) * 10**6,
                      max=max(durations) * 10**6,
                      ))
        create_boxplot('Reversing a string of length {}'.format(len(longstring)),
                       duration_list)


def list_comprehension(string):
    return string[::-1]


def reverse_func(string):
    return ''.join(reversed(string))


def reverse_reduce(string):
    return reduce(lambda x, y: y + x, string)


def reverse_loop(string):
    reversed_str = ""
    for i in string:
        reversed_str = i + reversed_str
    return reversed_str


def create_boxplot(title, duration_list, showfliers=False):
    import seaborn as sns
    import matplotlib.pyplot as plt
    import operator
    plt.figure(num=None, figsize=(8, 4), dpi=300,
               facecolor='w', edgecolor='k')
    sns.set(style="whitegrid")
    sorted_keys, sorted_vals = zip(*sorted(duration_list.items(),
                                           key=operator.itemgetter(1)))
    flierprops = dict(markerfacecolor='0.75', markersize=1,
                      linestyle='none')
    ax = sns.boxplot(data=sorted_vals, width=.3, orient='h',
                     flierprops=flierprops,
                     showfliers=showfliers)
    ax.set(xlabel="Time in ms", ylabel="")
    plt.yticks(plt.yticks()[0], sorted_keys)
    ax.set_title(title)
    plt.tight_layout()
    plt.savefig("output-string.png")


if __name__ == '__main__':
    main()

Recursive method:

def reverse(s): return s[0] if len(s)==1 else s[len(s)-1] + reverse(s[0:len(s)-1])

example:

print(reverse("Hello!"))    #!olleH

Here is a no fancy one:

def reverse(text):
    r_text = ''
    index = len(text) - 1

    while index >= 0:
        r_text += text[index] #string canbe concatenated
        index -= 1

    return r_text

print reverse("hello, world!")

This class uses python magic functions to reverse a string:

class Reverse(object):
    """ Builds a reverse method using magic methods """

    def __init__(self, data):
        self.data = data
        self.index = len(data)


    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration

        self.index = self.index - 1
        return self.data[self.index]


REV_INSTANCE = Reverse('hello world')

iter(REV_INSTANCE)

rev_str = ''
for char in REV_INSTANCE:
    rev_str += char

print(rev_str)  

Output

dlrow olleh

Reference


Reverse a string in python without using reversed() or [::-1]

def reverse(test):
    n = len(test)
    x=""
    for i in range(n-1,-1,-1):
        x += test[i]
    return x

This is simple and meaningful reverse function, easy to understand and code

def reverse_sentence(text):
    words = text.split(" ")
    reverse =""
    for word in reversed(words):
        reverse += word+ " "
    return reverse

Here is simply:

print "loremipsum"[-1::-1]

and some logically:

def str_reverse_fun():
    empty_list = []
    new_str = 'loremipsum'
    index = len(new_str)
    while index:
        index = index - 1
        empty_list.append(new_str[index])
    return ''.join(empty_list)
print str_reverse_fun()

output:

muspimerol


There are a lot of ways to reverse a string but I also created another one just for fun. I think this approach is not that bad.

def reverse(_str):
    list_char = list(_str) # Create a hypothetical list. because string is immutable

    for i in range(len(list_char)/2): # just t(n/2) to reverse a big string
        list_char[i], list_char[-i - 1] = list_char[-i - 1], list_char[i]

    return ''.join(list_char)

print(reverse("Ehsan"))

Here is one without [::-1] or reversed (for learning purposes):

def reverse(text):
    new_string = []
    n = len(text)
    while (n > 0):
        new_string.append(text[n-1])
        n -= 1
    return ''.join(new_string)
print reverse("abcd")

you can use += to concatenate strings but join() is faster.


A lesser perplexing way to look at it would be:

string = 'happy'
print(string)

'happy'

string_reversed = string[-1::-1]
print(string_reversed)

'yppah'

In English [-1::-1] reads as:

"Starting at -1, go all the way, taking steps of -1"


original = "string"

rev_index = original[::-1]
rev_func = list(reversed(list(original))) #nsfw

print(original)
print(rev_index)
print(''.join(rev_func))

1. using slice notation

def rev_string(s): 
    return s[::-1]

2. using reversed() function

def rev_string(s): 
    return ''.join(reversed(s))

3. using recursion

def rev_string(s): 
    if len(s) == 1:
        return s

    return s[-1] + rev_string(s[:-1])

Reverse a string without python magic.

>>> def reversest(st):
    a=len(st)-1
    for i in st:
        print(st[a],end="")
        a=a-1

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


s = 'hello'
ln = len(s)
i = 1
while True:
    rev = s[ln-i]
    print rev,
    i = i + 1
    if i == ln + 1 :
        break

OUTPUT :

o l l e h

Use a combination of a string array and a for loop:

s='Banana-Ana'

print(s[0]) #we can access each character of the string this way, where 0 is for the first letter, B in this case.
print(len(s)) #we can see the length of the string

#By combining above with a for loop, we get the reverse printed out, the old fashioned way:
for a in range(len(s)): print(s[len(s)-a-1])

def reverse_string(string):
    length = len(string)
    temp = ''
    for i in range(length):
        temp += string[length - i - 1]
    return temp

print(reverse_string('foo')) #prints "oof"

This works by looping through a string and assigning its values in reverse order to another string.


To solve this in programing way for interview

def reverse_a_string(string: str) -> str:
    """
    This method is used to reverse a string.
    Args:
        string: a string to reverse

    Returns: a reversed string
    """
    if type(string) != str:
        raise TypeError("{0} This not a string, Please provide a string!".format(type(string)))
    string_place_holder = ""
    start = 0
    end = len(string) - 1
    if end >= 1:
        while start <= end:
            string_place_holder = string_place_holder + string[end]
            end -= 1
        return string_place_holder
    else:
        return string


a = "hello world"
rev = reverse_a_string(a)
print(rev)

Output:

dlrow olleh

 a=input()
 print(a[::-1])

The above code recieves the input from the user and prints an output that is equal to the reverse of the input by adding [::-1].

OUTPUT:

>>> Happy 
>>> yppaH

But when it comes to the case of sentences, view the code output below:

>>> Have a happy day
>>> yad yppah a evaH

But if you want only the characters of the string to be reversed and not the sequence of string, try this:

a=input().split() #Splits the input on the basis of space (" ")
for b in a: #declares that var (b) is any value in the list (a)
    print(b[::-1], end=" ") #End declares to print the character in its quotes (" ") without a new line.

In the above code in line 2 in I said that ** variable b is any value in the list (a)** I said var a to be a list because when you use split in an input the variable of the input becomes a list. Also remember that split can't be used in the case of int(input())

OUTPUT:

>>> Have a happy day
>>> evaH a yppah yad

If we don't add end(" ") in the above code then it will print like the following:

>>> Have a happy day
>>> evaH
>>> a
>>> yppah
>>> yad

Below is an example to understand end():

CODE:

for i in range(1,6):
     print(i) #Without end()

OUTPUT:

>>> 1
>>> 2
>>> 3
>>> 4
>>> 5

Now code with end():

for i in range(1,6):
    print(i, end=" || ")

OUTPUT:

>>> 1 || 2 || 3 || 4 || 5 ||

This is also an interesting way:

def reverse_words_1(s):
    rev = ''
    for i in range(len(s)):
        j = ~i  # equivalent to j = -(i + 1)
        rev += s[j]
    return rev

or similar:

def reverse_words_2(s):
    rev = ''
    for i in reversed(range(len(s)):
        rev += s[i]
    return rev

Another more 'exotic' way using byterarray which supports .reverse()

b = bytearray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')

will produce:

'!siht esreveR'

Sure, in Python you can do very fancy 1-line stuff. :)
Here's a simple, all rounder solution that could work in any programming language.

def reverse_string(phrase):
    reversed = ""
    length = len(phrase)
    for i in range(length):
        reversed += phrase[length-1-i]
    return reversed

phrase = raw_input("Provide a string: ")
print reverse_string(phrase)

You can use the reversed function with a list comprehesive. But I don't understand why this method was eliminated in python 3, was unnecessarily.

string = [ char for char in reversed(string)]

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 @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 arrangement 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 start: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:] ## => '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 aspect of python.

See also


All of the above solutions are perfect but if we are trying to reverse a string using for loop in python will became a little bit tricky so here is how we can reverse a string using for loop

string ="hello,world"
for i in range(-1,-len(string)-1,-1):
    print (string[i],end=(" ")) 

I hope this one will be helpful for someone.