Related
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.
Using slicing:
>>> 'hello world'[::-1]
'dlrow olleh'
Slice notation takes the form [start:stop:step]. In this case, we omit the start and stop positions since we want the whole string. We also use step = -1, which means, "repeatedly step from right to left by 1 character".
#Paolo's s[::-1] is fastest; a slower approach (maybe more readable, but that's debatable) is ''.join(reversed(s)).
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:
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]
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.
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
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 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
alternate simple approach
alternate simple approach
alternate explanation of slice notation
This answer is a bit longer and contains 3 sections: Benchmarks of existing solutions, why most solutions here are wrong, my solution.
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:
NOTE: I've what I called list_comprehension should be called slicing
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).
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 python3
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()
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])
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"
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 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 bytearray which supports .reverse()
b = bytearray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')`
will produce:
'!siht esreveR'
def reverse(input):
return reduce(lambda x,y : y+x, input)
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!")
There are multiple ways to reverse a string in Python
Slicing Method
string = "python"
rev_string = string[::-1]
print(rev_string)
using reversed function
string = "python"
rev= reversed(string)
rev_string = "".join(rev)
print(rev_string)
Using Recursion
string = "python"
def reverse(string):
if len(string)==0:
return string
else:
return reverse(string[1:])+string[0]
print(reverse(string))
Using for Loop
string = "python"
rev_string =""
for s in string:
rev_string = s+ rev_string
print(rev_string)
Using while Loop
string = "python"
rev_str =""
length = len(string)-1
while length >=0:
rev_str += string[length]
length -= 1
print(rev_str)
original = "string"
rev_index = original[::-1]
rev_func = list(reversed(list(original))) #nsfw
print(original)
print(rev_index)
print(''.join(rev_func))
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
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
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.
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 ||
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=(" "))
Just as a different solution(because it's asked in interviews):
def reverse_checker(string):
ns = ""
for h in range(1,len(string)+1):
ns += string[-h]
print(ns)
if ns == string:
return True
else:
return False
Coming from the C/C++ world and being a Python newb, I wrote this simple string function that takes an input string (guaranteed to be ASCII) and returns the last four characters. If there’s less than four characters, I want to fill the leading positions with the letter ‘A'. (this was not an exercise, but a valuable part of another complex function)
There are dozens of methods of doing this, from brute force, to simple, to elegant. My approach below, while functional, didn’t seem "Pythonic".
NOTE: I’m presently using Python 2.6 — and performance is NOT an issue. The input strings are short (2-8 characters), and I call this function only a few thousand times.
def copyFourTrailingChars(src_str):
four_char_array = bytearray("AAAA")
xfrPos = 4
for x in src_str[::-1]:
xfrPos -= 1
four_char_array[xfrPos] = x
if xfrPos == 0:
break
return str(four_char_array)
input_str = "7654321"
print("The output of {0} is {1}".format(input_str, copyFourTrailingChars(input_str)))
input_str = "21"
print("The output of {0} is {1}".format(input_str, copyFourTrailingChars(input_str)))
The output is:
The output of 7654321 is 4321
The output of 21 is AA21
Suggestions from Pythoneers?
I would use simple slicing and then str.rjust() to right justify the result using A as fillchar . Example -
def copy_four(s):
return s[-4:].rjust(4,'A')
Demo -
>>> copy_four('21')
'AA21'
>>> copy_four('1233423')
'3423'
You can simple adding four sentinel 'A' character before the original string, then take the ending four characters:
def copy_four(s):
return ('AAAA'+s)[-4:]
That's simple enough!
How about something with string formatting?
def copy_four(s):
return '{}{}{}{}'.format(*('A'*(4-len(s[-4:])) + s[-4:]))
Result:
>>> copy_four('abcde')
'bcde'
>>> copy_four('abc')
'Aabc'
Here's a nicer, more canonical option:
def copy_four(s):
return '{:A>4}'.format(s[-4:])
Result:
>>> copy_four('abcde')
'bcde'
>>> copy_four('abc')
'Aabc'
You could use slicing to get the last 4 characters, then string repetition (* operator) and concatenation (+ operator) as below:
def trailing_four(s):
s = s[-4:]
s = 'A' * (4 - len(s)) + s
return s
You can try this
def copy_four_trailing_chars(input_string)
list_a = ['A','A','A','A']
str1 = input_string[:-4]
if len(str1) < 4:
str1 = "%s%s" % (''.join(list_a[:4-len(str1)]), str1)
return str1
I've been at this for a while now, and I've gotten so close.
I've had two problems to complete for an assignment. The first one I finished. It's supposed to return the first three indices of the given string, and if the string is less than 3 letters, it returns nothing. It looks like this:
The second one is similar, but a little more involved. The problem is:
Hopefully that explains my issue. I can't figure out how to get the inputted number to correspond to the number of indices I'm trying to print. In the first problem, it was simple, because it was always just the first three indices that were used. Now, it's (n) number of indices.
Any help is much appreciated (I'm using Python 3.4.2)
Strings support sub-stringing in Python.
def returnN(string, length):
return string[:length] if len(string) >= length else ''
In action:
>>> returnN('hello', 2)
'he'
>>> returnN('hello', 5)
'hello'
>>> returnN('BYE', 1)
'B'
>>> returnN('BYE', 10)
''
Use len and slice method of string method like:
def returnN(string, length):
length_string = len(string)
if length > length_string:
return ''
return string[0:length]
print(returnN('hello', 5))
print(returnN('hello', 2))
print(returnN('Nye', 1))
print(returnN('OKOK', 10))
or simple way:
def returnN_S(string, length):
return string[: length] if length <= len(string) else ''
print(returnN_S('hello', 5))
print(returnN_S('hello', 2))
print(returnN_S('Nye', 1))
print(returnN_S('OKOK', 10))
or one line way:
returnN_H = lambda string, length: string[: length] if length <= len(string) else ''
print(returnN_H('hello', 5))
print(returnN_H('hello', 2))
print(returnN_H('Nye', 1))
print(returnN_H('OKOK', 10))
Hope helps.
Unicode strings in Python are immutable Unicode codepoint sequences.
Unicode codepoint is just a number from 0 to sys.maxunicode e.g., Nº128516: 😄('\U0001f604').
A substring/slice of a Unicode string s such as s[:2] returns a new Unicode string that contains 2 Unicode codepoints.
len(s) returns the number of Unicode codepoints in the string s.
An empty string could be represented using '' string literal in Python source code.
To return a given number of user-perceived characters from a string or an empty string if the text size is too small, you could use \X regular expression (it matches an eXtended grapheme cluster):
#!/usr/bin/env python3
import regex # $ pip install regex
def returnN(text, n):
chars = regex.findall(r'\X', text)
return ''.join(chars[:n]) if len(chars) >= n else ''
text = 'a\u0300 biento\u0302t'
print(text) # -> à bientôt
print(returnN(text, 4)) # -> à bi
A single user-perceived character such as à can span several Unicode codepoints such as U+0061, U+0300.
The term character is ambiguous. It can mean a byte, Unicode codepoint, grapheme cluster in different circumstances.
input_string = "hello"
def returnN(string, n):
return string[:n]
output = returnN(input_string, 3)
that's all
I'm trying to write a basic algorithm for encrypting a file. It takes the ASCII value of each character in a string and moves it up or down an amount depending on how long the password is, then you can layer more passwords on top.
def encrypt(s):
lenStr=s.__len__() #used later for working how far the int is moved
s=list(s) #converts the string to a list
for x in s:
s[x]=ord(s[x]) #the same index of the list is = to the value of the string
s[x]=chr(s[x])#is where it eventualy gets changed back to a str
s=ord(s) is the line which is throwing the error, i added int() around it but didnt help, same error
You're getting theTypeErrorexception because the value ofxin thes[x]=ord(s[x]) statement is one of the elements of the s list, so it's an individual character from the string argument passed toencrypt(). To fix that, just loop through all the possible indices of the s list which happens to be the same as the length as the original string:
def encrypt(s):
lenStr=len(s)
s=list(s) # convert the string to a list
for i in range(lenStr):
s[i]=ord(s[i])
s[i]=chr(s[i])
This will allow your code to run without getting that error. From your description of the encryption algorithm you're going to implement, one thing to watch out for is producing illegal 8-bit character values out of the range of 0-255. You can avoid that problem by simply applying the mod operator % to the intermediate results to keep the values in the proper range. Here's what I mean:
def encrypt(s):
lenStr = len(s)
s = list(s) # convert the string to a list
for i in range(lenStr):
s[i] = chr((ord(s[i]) + lenStr) % 256)
return ''.join(s) # convert list back into a string
Likewise, you'll have to do the same thing when you decrypt a string:
def decrypt(s):
lenStr = len(s)
s = list(s) # convert the string to a list
for i in range(lenStr):
s[i] = chr((ord(s[i]) - lenStr) % 256)
return ''.join(s) # convert list back into a string
enc = encrypt('Gnomorian')
print('encrypted:', enc)
dec = decrypt(enc)
print('decrypted:', dec)
Output:
encrypted: Pwxvx{rjw
decrypted: Gnomorian
Also note that not all the characters whose ord() values are in the range of 0-255 are printable, so you may want to restrict the encryption transformation even more if that's a requirement (that the encrypted version be printable).
x is a character from the string, not an integer. Let me illustrate:
>>> s = list('abcd')
>>> for x in s:
... print(x)
...
a
b
c
d
>>>
You want x to be integer values from 0 to the length of the string, like this:
>>> for x in range(len(s)):
... print(x)
...
0
1
2
3
>>>
So, your function should probably look like this (untested):
def encrypt(s):
lenStr=s.__len__() #used later for working how far the int is moved
s=list(s) #converts the string to a list
for x in range(len(s)):
s[x]=ord(s[x]) #the same index of the list is = to the value of the string
s[x]=chr(s[x])#is where it eventualy gets changed back to a str
I am guessing this is what you are aiming for:
def encrypt(s):
offset = len(s)
return ''.join(chr(ord(c) + offset) for c in s)
def decrypt(s):
offset = len(s)
return ''.join(chr(ord(c) - offset) for c in s)
Some tips:
Use len(s) instead of lenStr=s.__len__()
Naming values near their first use in the code improves readability.
Choose names that describe the use of the value.
Strings are iterable, same as lists. No need to convert a string into a list.
Learn and use list comprehensions and generators whenever possible, they are usually much faster, simpler, easier to read and less error prone to create.
Remember to accept and/or upvote answers that are helpful.
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.
Using slicing:
>>> 'hello world'[::-1]
'dlrow olleh'
Slice notation takes the form [start:stop:step]. In this case, we omit the start and stop positions since we want the whole string. We also use step = -1, which means, "repeatedly step from right to left by 1 character".
#Paolo's s[::-1] is fastest; a slower approach (maybe more readable, but that's debatable) is ''.join(reversed(s)).
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:
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]
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.
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
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 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
alternate simple approach
alternate simple approach
alternate explanation of slice notation
This answer is a bit longer and contains 3 sections: Benchmarks of existing solutions, why most solutions here are wrong, my solution.
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:
NOTE: I've what I called list_comprehension should be called slicing
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).
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 python3
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()
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])
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"
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 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 bytearray which supports .reverse()
b = bytearray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')`
will produce:
'!siht esreveR'
def reverse(input):
return reduce(lambda x,y : y+x, input)
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!")
There are multiple ways to reverse a string in Python
Slicing Method
string = "python"
rev_string = string[::-1]
print(rev_string)
using reversed function
string = "python"
rev= reversed(string)
rev_string = "".join(rev)
print(rev_string)
Using Recursion
string = "python"
def reverse(string):
if len(string)==0:
return string
else:
return reverse(string[1:])+string[0]
print(reverse(string))
Using for Loop
string = "python"
rev_string =""
for s in string:
rev_string = s+ rev_string
print(rev_string)
Using while Loop
string = "python"
rev_str =""
length = len(string)-1
while length >=0:
rev_str += string[length]
length -= 1
print(rev_str)
original = "string"
rev_index = original[::-1]
rev_func = list(reversed(list(original))) #nsfw
print(original)
print(rev_index)
print(''.join(rev_func))
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
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
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.
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 ||
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=(" "))
Just as a different solution(because it's asked in interviews):
def reverse_checker(string):
ns = ""
for h in range(1,len(string)+1):
ns += string[-h]
print(ns)
if ns == string:
return True
else:
return False