Was ist der beste Weg, um eine Umkehrfunktion für Strings zu implementieren?
Meine eigene Erfahrung mit dieser Frage ist akademisch. Wenn Sie jedoch ein Profi sind, der nach einer schnellen Antwort sucht, verwenden Sie ein Slice, das folgende Schritte umfasst -1
:
>>> 'a string'[::-1]
'gnirts a'
oder besser lesbar (aber langsamer aufgrund der Suche nach Methodennamen und der Tatsache, dass Join eine Liste bildet, wenn ein Iterator angegeben wird) , str.join
:
>>> ''.join(reversed('a string'))
'gnirts a'
oder setzen Sie das Slice zur besseren Lesbarkeit und Wiederverwendbarkeit in eine Funktion ein
def reversed_string(a_string):
return a_string[::-1]
und dann:
>>> reversed_string('a_string')
'gnirts_a'
Längere Erklärung
Wenn Sie an der akademischen Ausstellung interessiert sind, lesen Sie bitte weiter.
In Pythons str-Objekt ist keine Umkehrfunktion integriert.
Hier sind einige Dinge über Pythons Zeichenfolgen, die Sie wissen sollten:
In Python sind Zeichenfolgen unveränderlich . Durch Ändern einer Zeichenfolge wird die Zeichenfolge nicht geändert. Es schafft eine neue.
Saiten sind in Scheiben geschnitten. Wenn Sie eine Zeichenfolge in Scheiben schneiden, erhalten Sie eine neue Zeichenfolge von einem Punkt in der Zeichenfolge vorwärts oder rückwärts zu einem anderen Punkt in bestimmten Schritten. Sie nehmen die Slice-Notation oder ein Slice-Objekt in einem Index an:
string[subscript]
Der Index erstellt ein Slice, 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]
Ein lesbarer Ansatz:
Während ''.join(reversed('foo'))
es lesbar ist, muss eine String-Methode str.join
für eine andere aufgerufene Funktion aufgerufen werden, die relativ langsam sein kann. Lassen Sie uns dies in eine Funktion einfügen - wir werden darauf zurückkommen:
def reverse_string_readable_answer(string):
return ''.join(reversed(string))
Performantester Ansatz:
Viel schneller ist die Verwendung eines umgekehrten Slice:
'foo'[::-1]
Aber wie können wir dies für jemanden lesbarer und verständlicher machen, der mit Slices oder der Absicht des ursprünglichen Autors weniger vertraut ist? Erstellen wir ein Slice-Objekt außerhalb der Indexnotation, geben Sie ihm einen beschreibenden Namen und übergeben Sie es an die Indexnotation.
start = stop = None
step = -1
reverse_slice = slice(start, stop, step)
'foo'[reverse_slice]
Als Funktion implementieren
Um dies tatsächlich als Funktion zu implementieren, denke ich, dass es semantisch klar genug ist, einfach einen beschreibenden Namen zu verwenden:
def reversed_string(a_string):
return a_string[::-1]
Und die Nutzung ist einfach:
reversed_string('foo')
Was Ihr Lehrer wahrscheinlich will:
Wenn Sie einen Lehrer haben, möchten diese wahrscheinlich, dass Sie mit einer leeren Zeichenfolge beginnen und eine neue Zeichenfolge aus der alten erstellen. Sie können dies mit reiner Syntax und Literalen mithilfe 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, da Zeichenfolgen unveränderlich sind. Jedes Mal, wenn es so aussieht, als würden Sie einen Charakter an Ihre new_string
Zeichenfolge anhängen , wird theoretisch jedes Mal eine neue Zeichenfolge erstellt. CPython weiß jedoch, wie dies in bestimmten Fällen optimiert werden kann, von denen dieser triviale Fall einer ist.
Beste Übung
Theoretisch ist es besser, Ihre Teilzeichenfolgen in einer Liste zu sammeln und später zu verbinden:
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 folgenden Zeitangaben für CPython sehen werden, dauert dies tatsächlich länger, da CPython die Verkettung von Zeichenfolgen optimieren kann.
Timings
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, während andere Implementierungen möglicherweise nicht :
... verlassen Sie sich nicht auf CPythons effiziente Implementierung der direkten Verkettung von Zeichenfolgen für Anweisungen in der Form a + = b oder a = a + b. Diese Optimierung ist selbst in CPython fragil (funktioniert nur bei einigen Typen) und ist in Implementierungen ohne Refcounting überhaupt nicht vorhanden. In leistungsempfindlichen Teilen der Bibliothek sollte stattdessen das Formular '' .join () verwendet werden. Dadurch wird sichergestellt, dass die Verkettung in linearer Zeit über verschiedene Implementierungen hinweg erfolgt.