Il n'y a pas de fonction reverse
intégrée pour l'objet str
de Python. Quel est le meilleur moyen de mettre en œuvre cette méthode?
Si vous fournissez une réponse très concise, veuillez préciser son efficacité. Par exemple, si l'objet str
est converti en un objet différent, etc.
Que diriez-vous:
>>> 'hello world'[::-1]
'dlrow olleh'
C'est extended slice syntax. Cela fonctionne en faisant [begin:end:step]
- en laissant début et fin et en spécifiant un pas de -1, il inverse une chaîne.
Le s[::-1]
de @ Paolo est le plus rapide; une approche plus lente (peut-être plus lisible, mais c'est discutable) est ''.join(reversed(s))
.
Quel est le meilleur moyen d'implémenter une fonction inverse pour les chaînes?
Ma propre expérience avec cette question est académique. Toutefois, si vous êtes un professionnel à la recherche de la réponse rapide, utilisez une tranche qui passe par -1
:
>>> 'a string'[::-1]
'gnirts a'
ou plus lisiblement (mais plus lentement à cause des recherches de nom de méthode et du fait que join forme une liste quand un itérateur est donné), str.join
:
>>> ''.join(reversed('a string'))
'gnirts a'
ou pour des raisons de lisibilité et de réutilisabilité, placez la tranche dans une fonction
def reversed_string(a_string):
return a_string[::-1]
et alors:
>>> reversed_string('a_string')
'gnirts_a'
Si vous êtes intéressé par l'exposition académique, continuez à lire.
Il n'y a pas de fonction inverse intégrée dans l'objet str de Python.
Voici quelques informations sur les chaînes de Python que vous devriez connaître:
En Python, les chaînes sont immuables. Changer une chaîne ne modifie pas la chaîne. Cela en crée un nouveau.
Les cordes sont sliceable. Trancher une chaîne vous donne une nouvelle chaîne d'un point dans la chaîne, en avant ou en arrière, à un autre point, par incréments donnés. Ils prennent la notation slice ou un objet slice en indice:
string[subscript]
L'indice crée une tranche en incluant deux points entre les accolades:
string[start:stop:step]
Pour créer une tranche en dehors des accolades, vous devez créer un objet tranche:
slice_obj = slice(start, stop, step)
string[slice_obj]
Alors que ''.join(reversed('foo'))
est lisible, il faut appeler une méthode de chaîne, str.join
, sur une autre fonction appelée, qui peut être relativement lente. Mettons cela dans une fonction - nous y reviendrons:
def reverse_string_readable_answer(string):
return ''.join(reversed(string))
Beaucoup plus rapidement utilise une tranche inversée:
'foo'[::-1]
Mais comment pouvons-nous rendre cela plus lisible et compréhensible pour quelqu'un de moins au courant des tranches ou de l'intention de l'auteur original? Créons un objet slice en dehors de la notation en indice, attribuons-lui un nom descriptif et passons-le à la notation en indice.
start = stop = None
step = -1
reverse_slice = slice(start, stop, step)
'foo'[reverse_slice]
Pour réellement implémenter cela en tant que fonction, je pense qu'il est suffisamment clair sur le plan sémantique pour utiliser simplement un nom descriptif:
def reversed_string(a_string):
return a_string[::-1]
Et l'utilisation est simplement:
reversed_string('foo')
Si vous avez un instructeur, il voudra probablement que vous commenciez avec une chaîne vide et que vous construisiez une nouvelle chaîne à partir de l’ancienne. Vous pouvez le faire avec de la syntaxe pure et des littéraux en utilisant une boucle while:
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
Ceci est théoriquement mauvais parce que, rappelez-vous, les chaînes sont immuables - donc chaque fois que vous ajoutez un caractère à votre new_string
, vous créez théoriquement une nouvelle chaîne à chaque fois! Cependant, CPython sait comment optimiser cela dans certains cas, dont ce cas trivial.
Théoriquement, il est préférable de rassembler vos sous-chaînes dans une liste et de les rejoindre plus tard:
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)
Toutefois, comme nous le verrons dans les timings ci-dessous pour CPython, cela prend plus de temps, car CPython peut optimiser la concaténation de chaînes.
Voici les 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 optimise la concaténation de chaînes, alors que d'autres implémentations peuvent ne pas :
... ne vous fiez pas à la mise en oeuvre efficace par CPython de la concaténation sur place de chaînes pour les instructions de la forme a + = b ou a = a + b. Cette optimisation est fragile même dans CPython (cela ne fonctionne que pour certains types) et n'est pas présente du tout dans les implémentations qui n'utilisent pas refcounting. Dans les parties de la bibliothèque sensibles aux performances, le formulaire '' .join () doit être utilisé à la place. Cela garantira que la concaténation se produise dans le temps linéaire à travers différentes implémentations.
### example01 -------------------
mystring = 'coup_ate_grouping'
backwards = mystring[::-1]
print backwards
### ... or even ...
mystring = 'coup_ate_grouping'[::-1]
print mystring
### result01 -------------------
'''
gnipuorg_eta_puoc
'''
Cette réponse est fournie pour répondre à la préoccupation suivante de @odigity:
Sensationnel. J'ai d'abord été horrifié par la solution proposée par Paolo, mais cela pris la banquette arrière de l'horreur que j'ai ressentie en lisant le premier commentaire: "C'est très Pythonique. Bon travail!" Je suis tellement dérangé que tel une communauté brillante pense utiliser de telles méthodes cryptiques pour quelque chose de tellement la base est une bonne idée. Pourquoi n'est-ce pas juste à l'inverse ()?
string.reverse()
string.reverse()
pour éviter la notation par tranches.print 'coup_ate_grouping'[-4:] ## => 'ping'
print 'coup_ate_grouping'[-4:-1] ## => 'pin'
print 'coup_ate_grouping'[-1] ## => 'g'
[-1]
peuvent décourager certains développeursPython a une circonstance spéciale à prendre en compte: une chaîne est un type iterable .
Une des raisons pour exclure une méthode string.reverse()
est d'encourager les développeurs python à tirer parti de la puissance de cette circonstance particulière.
En termes simplifiés, cela signifie simplement que chaque caractère d'une chaîne peut être facilement utilisé dans le cadre d'une disposition séquentielle d'éléments, tout comme les tableaux dans d'autres langages de programmation.
Pour comprendre comment cela fonctionne, vous pouvez passer en revue exemple02 pour obtenir un bon aperçu.
### 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'
La charge cognitive associée à la compréhension du fonctionnement de la notation slice en python est peut-être trop pour certains adoptants et développeurs qui ne souhaitent pas investir beaucoup de temps dans l'apprentissage du langage.
Néanmoins, une fois que les principes de base sont compris, le pouvoir de cette approche par rapport aux méthodes de manipulation de chaînes fixes peut être très favorable.
Pour ceux qui pensent le contraire, il existe des approches alternatives, telles que les fonctions lambda, les itérateurs ou les déclarations de fonctions simples et ponctuelles.
Si cela est souhaité, un développeur peut implémenter sa propre méthode string.reverse (), mais il est bon de comprendre la logique qui sous-tend cet aspect de python.
Une façon moins compliquée de voir les choses serait:
string = 'happy'
print(string)
'heureux'
string_reversed = string[-1::-1]
print(string_reversed)
'yppah'
En anglais [-1 :: - 1] se lit comme suit:
"À partir de -1, allez jusqu'au bout, en prenant des mesures de -1"
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])
Inverser une chaîne en python sans utiliser reverse () ou [:: - 1]
def reverse(test):
n = len(test)
x=""
for i in range(n-1,-1,-1):
x += test[i]
return x
def reverse(input):
return reduce(lambda x,y : y+x, input)
C'est aussi un moyen intéressant:
def reverse_words_1(s):
rev = ''
for i in range(len(s)):
j = ~i # equivalent to j = -(i + 1)
rev += s[j]
return rev
ou similaire:
def reverse_words_2(s):
rev = ''
for i in reversed(range(len(s)):
rev += s[i]
return rev
Une autre manière plus "exotique" d'utiliser Byterarray qui prend en charge .reverse ()
b = byterarray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')`
produira:
'!siht esreveR'
Les réponses existantes ne sont correctes que si les modificateurs Unicode/grappes de graphèmes sont ignorés. J'y reviendrai plus tard, mais examinons d'abord la vitesse de certains algorithmes d'inversion:
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
Vous pouvez voir que le temps pour la compréhension de la liste (reversed = string[::-1]
) est dans tous les cas de loin le plus bas (même après avoir corrigé ma faute de frappe).
Si vous voulez vraiment inverser une chaîne au sens commun, c'est beaucoup plus compliqué. Par exemple, prenons la chaîne suivante ( doigt brun pointant à gauche _ _ doigt jaune pointant vers le haut ). Ce sont deux graphèmes, mais 3 points de code Unicode. Le complément est un modificateur de peau .
example = "????????????"
Mais si vous inversez avec l'une des méthodes données, vous obtenez le doigt brun qui pointe vers le haut , le doigt jaune qui pointe vers la gauche . La raison en est que le modificateur de couleur "marron" est toujours au milieu et est appliqué à tout ce qui se trouve devant lui. Donc nous avons
et
original: LMU
reversed: UML (above solutions)
reversed: ULM (correct reversal)
_ { Unicode Grapheme Clusters } _ sont un peu plus compliqués que de simples points de code modificateurs. Heureusement, il existe une bibliothèque pour manipuler graphemes :
>>> import grapheme
>>> g = grapheme.graphemes("????????????")
>>> list(g)
['????????', '????']
et donc la bonne réponse serait
def reverse_graphemes(string):
g = list(grapheme.graphemes(string))
return ''.join(g[::-1])
qui est aussi de loin le plus lent:
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 __== '__main__':
main()
Voici un pas de fantaisie:
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!")
C’est ma façon:
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
Toutes les solutions ci-dessus sont parfaites, mais si nous essayons d'inverser une chaîne en utilisant une boucle for en python, cela deviendra un peu délicat.
string ="hello,world"
for i in range(-1,-len(string)-1,-1):
print (string[i],end=(" "))
J'espère que celui-ci sera utile à quelqu'un.
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"
Cela fonctionne en boucle sur une chaîne et en affectant ses valeurs dans l'ordre inverse à une autre chaîne.
original = "string"
rev_index = original[::-1]
rev_func = list(reversed(list(original))) #nsfw
print(original)
print(rev_index)
print(''.join(rev_func))
Il y a beaucoup de façons d'inverser une chaîne, mais j'ai aussi créé une autre juste pour le fun. Je pense que cette approche n'est pas si mauvaise.
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"))
Méthode récursive:
def reverse(s): return s[0] if len(s)==1 else s[len(s)-1] + reverse(s[0:len(s)-1])
exemple:
print(reverse("Hello!")) #!olleH
En voici un sans [::-1]
ou reversed
(à des fins d'apprentissage):
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")
vous pouvez utiliser +=
pour concaténer des chaînes, mais join()
est plus rapide.
Inverser une chaîne sans magie python.
>>> def reversest(st):
a=len(st)-1
for i in st:
print(st[a],end="")
a=a-1