Für das reverse
-Objekt von Python ist keine str
-Funktion eingebaut. Wie implementiere ich diese Methode am besten?
Wenn Sie eine sehr präzise Antwort geben, erläutern Sie bitte deren Effizienz. Zum Beispiel, ob das str
-Objekt in ein anderes Objekt konvertiert wird usw.
Wie wäre es mit:
>>> 'hello world'[::-1]
'dlrow olleh'
Dies ist Extended Slice Syntax. Es funktioniert, indem [begin:end:step]
ausgeführt wird. Indem Sie begin und end off verlassen und einen Schritt von -1 angeben, wird eine Zeichenfolge umgekehrt.
@ Paolos s[::-1]
ist am schnellsten; ein langsamerer Ansatz (vielleicht lesbarer, aber das ist fraglich) ist ''.join(reversed(s))
.
Wie implementiere ich am besten eine Reverse-Funktion für Strings?
Meine eigenen Erfahrungen mit dieser Frage sind wissenschaftlich. Wenn Sie jedoch nach einem Profi suchen, der nach einer schnellen Antwort sucht, verwenden Sie ein Slice, das durch -1
geführt wird:
>>> 'a string'[::-1]
'gnirts a'
oder lesbarer (aber langsamer aufgrund der Methodennamensuche und der Tatsache, dass join bei einem Iterator eine Liste bildet), str.join
:
>>> ''.join(reversed('a string'))
'gnirts a'
oder zur besseren Lesbarkeit und Wiederverwendbarkeit legen Sie die Slice in eine Funktion
def reversed_string(a_string):
return a_string[::-1]
und dann:
>>> reversed_string('a_string')
'gnirts_a'
Wenn Sie sich für die akademische Ausstellung interessieren, lesen Sie bitte weiter.
Im str-Objekt von Python gibt es keine integrierte Umkehrfunktion.
Hier sind ein paar Dinge über Pythons Saiten, die Sie kennen sollten:
In Python sind Zeichenfolgen unveränderlich. Das Ändern einer Zeichenfolge ändert die Zeichenfolge nicht. Es entsteht eine neue.
Strings können geschnitten werden. Beim Aufteilen einer Zeichenfolge erhalten Sie eine neue Zeichenfolge von einem Punkt in der Zeichenfolge (vorwärts oder rückwärts) zu einem anderen Punkt, und zwar in bestimmten Schritten. Sie nehmen Slice-Notation oder ein Slice-Objekt in einem Index:
string[subscript]
Der Index erstellt ein Segment, indem ein Doppelpunkt in die geschweiften Klammern eingefügt wird:
string[start:stop:step]
Um ein Slice außerhalb der geschweiften Klammern zu erstellen, müssen Sie ein Slice-Objekt erstellen:
slice_obj = slice(start, stop, step)
string[slice_obj]
Während ''.join(reversed('foo'))
lesbar ist, muss für eine andere aufgerufene Funktion eine String-Methode str.join
aufgerufen werden, die relativ langsam sein kann. Lassen Sie uns dies in eine Funktion einfügen - wir kommen darauf zurück:
def reverse_string_readable_answer(string):
return ''.join(reversed(string))
Viel schneller geht es mit einem Reverse-Slice:
'foo'[::-1]
Aber wie können wir das für jemanden, der mit Slices oder der Absicht des ursprünglichen Autors weniger vertraut ist, lesbarer und verständlicher machen? Erstellen wir ein Slice-Objekt außerhalb der Indexnotation, geben Sie einen beschreibenden Namen und übergeben Sie es an die Indexnotation.
start = stop = None
step = -1
reverse_slice = slice(start, stop, step)
'foo'[reverse_slice]
Um dies tatsächlich als Funktion zu implementieren, ist es meiner Meinung nach semantisch klar genug, einfach einen beschreibenden Namen zu verwenden:
def reversed_string(a_string):
return a_string[::-1]
Und die Verwendung ist einfach:
reversed_string('foo')
Wenn Sie einen Lehrer haben, möchten Sie wahrscheinlich, dass Sie mit einer leeren Zeichenfolge beginnen und aus der alten eine neue Zeichenfolge erstellen. Sie können dies mit reiner Syntax und Literalen mit einer while-Schleife tun:
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
Dies ist theoretisch schlecht, weil Zeichenfolgen unveränderlich sind - also jedes Mal, wenn Sie ein Zeichen an Ihren new_string
anhängen, wird theoretisch jedes Mal eine neue Zeichenfolge erstellt! CPython kann dies jedoch in bestimmten Fällen optimieren, von denen dieser triviale Fall ein Fall ist.
Theoretisch ist es besser, die Teilzeichenfolgen in einer Liste zu sammeln und später zusammenzufügen:
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)
Wie wir jedoch in den Timings unten für CPython sehen werden, dauert dies tatsächlich länger, da CPython die String-Verkettung optimieren kann.
Hier sind die 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 optimiert die Verkettung von Zeichenfolgen, wohingegen andere Implementierungen möglicherweise nicht :
... verlassen Sie sich nicht auf CPythons effiziente Implementierung der In-Place-String-Verkettung für Anweisungen in der Form a + = b oder a = a + b. Diese Optimierung ist selbst in CPython fragil (sie funktioniert nur für einige Typen) und ist in Implementierungen, die kein Refcounting verwenden, überhaupt nicht vorhanden. In leistungsabhängigen Teilen der Bibliothek sollte stattdessen das Formular '' .join () verwendet werden. Dadurch wird sichergestellt, dass die Verkettung über verschiedene Implementierungen hinweg linear erfolgt.
### example01 -------------------
mystring = 'coup_ate_grouping'
backwards = mystring[::-1]
print backwards
### ... or even ...
mystring = 'coup_ate_grouping'[::-1]
print mystring
### result01 -------------------
'''
gnipuorg_eta_puoc
'''
Diese Antwort wird bereitgestellt, um das folgende Anliegen von @odigity anzusprechen:
Beeindruckend. Ich war zuerst entsetzt über die von Paolo vorgeschlagene Lösung, aber das setzte sich hinter das Grauen zurück, das ich beim ersten Lesen empfand. Kommentar: "Das ist sehr pythonisch. Gute Arbeit!" Ich bin so gestört, dass so eine kluge Gemeinschaft denkt, solche kryptischen Methoden für so etwas einzusetzen. Basic ist eine gute Idee. Warum liegt es nicht nur an der Gegenseite ()?
string.reverse()
string.reverse()
zu implementieren, um die Slice-Notation zu vermeiden.print 'coup_ate_grouping'[-4:] ## => 'ping'
print 'coup_ate_grouping'[-4:-1] ## => 'pin'
print 'coup_ate_grouping'[-1] ## => 'g'
[-1]
können einige Entwickler abschreckenIn Python ist ein besonderer Umstand zu beachten: Ein String ist vom Typ iterable .
Ein Grund für den Ausschluss einer string.reverse()
-Methode ist, Python-Entwicklern einen Anreiz zu geben, die Möglichkeiten dieses besonderen Umstands zu nutzen.
Vereinfacht ausgedrückt bedeutet dies einfach, dass jedes einzelne Zeichen in einer Zeichenfolge genauso wie Arrays in anderen Programmiersprachen als Teil einer sequentiellen Anordnung von Elementen bearbeitet werden kann.
Um zu verstehen, wie dies funktioniert, kann das Überprüfen von example02 einen guten Überblick geben.
### 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'
Das cognitive load , das mit dem Verständnis der Funktionsweise der Slice-Notation in Python verbunden ist, kann für einige Anwender und Entwickler, die nicht viel Zeit in das Erlernen der Sprache investieren möchten, in der Tat zu viel sein.
Sobald jedoch die Grundprinzipien verstanden sind, kann die Macht dieses Ansatzes gegenüber festen String-Manipulationsverfahren durchaus günstig sein.
Für diejenigen, die anders denken, gibt es alternative Ansätze, z. B. Lambda-Funktionen, Iteratoren oder einfache Funktionsdeklarationen.
Wenn gewünscht, kann ein Entwickler seine eigene string.reverse () -Methode implementieren, es ist jedoch gut, die Gründe für diesen Aspekt von Python zu verstehen.
Eine weniger verblüffende Sichtweise wäre:
string = 'happy'
print(string)
'glücklich'
string_reversed = string[-1::-1]
print(string_reversed)
'Yppah'
In Englisch [-1 :: - 1] liest als:
"Beginnen Sie mit -1, gehen Sie den ganzen Weg und nehmen Sie die Schritte von -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])
Umkehren eines Strings in Python ohne Verwendung von reversed () oder [:: - 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)
Dies ist auch ein interessanter Weg:
def reverse_words_1(s):
rev = ''
for i in range(len(s)):
j = ~i # equivalent to j = -(i + 1)
rev += s[j]
return rev
oder ähnliches:
def reverse_words_2(s):
rev = ''
for i in reversed(range(len(s)):
rev += s[i]
return rev
Eine weitere "exotische" Methode, bei der byterarray verwendet wird.
b = byterarray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')`
wird herstellen:
'!siht esreveR'
Die vorhandenen Antworten sind nur korrekt, wenn Unicode-Modifikatoren/Graphem-Cluster ignoriert werden. Darauf werde ich später noch eingehen, aber schauen Sie sich zuerst die Geschwindigkeit einiger Umkehralgorithmen an:
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
Sie können sehen, dass die Zeit für das Listenverständnis (reversed = string[::-1]
) in allen Fällen bei weitem am niedrigsten ist (auch nachdem ich meinen Tippfehler behoben habe).
Wenn Sie wirklich eine Zeichenfolge im gesunden Menschenverstand umkehren möchten, ist es viel komplizierter. Nehmen Sie zum Beispiel die folgende Zeichenfolge ( brauner Finger zeigt nach links , gelber Finger zeigt nach oben ). Das sind zwei Grapheme, aber 3 Unicode-Codepunkte. Das zusätzliche ist ein Skin Modifier .
example = "????????????"
Aber wenn Sie es mit einer der angegebenen Methoden umkehren, erhalten Sie braunen Finger nach oben , gelben Finger nach links . Der Grund dafür ist, dass der "braune" Farbmodifikator immer noch in der Mitte ist und auf alles angewendet wird, was sich davor befindet. Also haben wir
und
original: LMU
reversed: UML (above solutions)
reversed: ULM (correct reversal)
Unicode Grapheme-Cluster sind etwas komplizierter als nur Modifikator-Codepunkte. Zum Glück gibt es eine Bibliothek zum Umgang mit Graphemen :
>>> import grapheme
>>> g = grapheme.graphemes("????????????")
>>> list(g)
['????????', '????']
und daher wäre die richtige Antwort
def reverse_graphemes(string):
g = list(grapheme.graphemes(string))
return ''.join(g[::-1])
welches auch bei weitem das langsamste ist:
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()
Hier ist nichts Besonderes:
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!")
Das ist mein Weg:
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
Alle oben genannten Lösungen sind perfekt, aber wenn Sie versuchen, eine Zeichenfolge mit for-Schleife in Python umzukehren, wird dies ein wenig schwierig. Daher können Sie eine Zeichenfolge mit for-Schleife umkehren
string ="hello,world"
for i in range(-1,-len(string)-1,-1):
print (string[i],end=(" "))
Ich hoffe, dieser wird für jemanden hilfreich sein.
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"
Dies geschieht, indem Sie einen String durchlaufen und seine Werte in umgekehrter Reihenfolge einem anderen String zuweisen.
original = "string"
rev_index = original[::-1]
rev_func = list(reversed(list(original))) #nsfw
print(original)
print(rev_index)
print(''.join(rev_func))
Es gibt viele Möglichkeiten, eine Saite umzukehren, aber ich habe auch aus Spaß eine andere erstellt. Ich denke, dieser Ansatz ist nicht so schlecht.
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"))
Rekursive Methode:
def reverse(s): return s[0] if len(s)==1 else s[len(s)-1] + reverse(s[0:len(s)-1])
beispiel:
print(reverse("Hello!")) #!olleH
Hier ist eine ohne [::-1]
oder reversed
(zu Lernzwecken):
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")
sie können +=
verwenden, um Zeichenfolgen zu verketten, aber join()
ist schneller.
Kehre eine Saite ohne Python-Magie um.
>>> def reversest(st):
a=len(st)-1
for i in st:
print(st[a],end="")
a=a-1