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.
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'
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:
In Python, strings are immutable. Changing a string does not modify the string. It creates a new one.
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]
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))
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]
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')
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.
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.
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:
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
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).
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
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
#!/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)
dlrow olleh
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))
def rev_string(s):
return s[::-1]
def rev_string(s):
return ''.join(reversed(s))
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)]
### example01 -------------------
mystring = 'coup_ate_grouping'
backwards = mystring[::-1]
print backwards
### ... or even ...
mystring = 'coup_ate_grouping'[::-1]
print mystring
### result01 -------------------
'''
gnipuorg_eta_puoc
'''
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()?
string.reverse()
string.reverse()
to avoid slice notation.print 'coup_ate_grouping'[-4:] ## => 'ping'
print 'coup_ate_grouping'[-4:-1] ## => 'pin'
print 'coup_ate_grouping'[-1] ## => 'g'
[-1]
may throw some developers offPython 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 -------------------
## 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'
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.
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.
Source: Stackoverflow.com