it-swarm.com.de

Umkehren einer Zeichenfolge in Python

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. 

1190
oneself

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.

2430

@ Paolos s[::-1] ist am schnellsten; ein langsamerer Ansatz (vielleicht lesbarer, aber das ist fraglich) ist ''.join(reversed(s)).

236
Alex Martelli

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'

Längere Erklärung

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:

  1. In Python sind Zeichenfolgen unveränderlich. Das Ändern einer Zeichenfolge ändert die Zeichenfolge nicht. Es entsteht eine neue.

  2. 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]

Ein lesbarer Ansatz:

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))

Performantster Ansatz:

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]

Als Funktion implementieren

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')

Was Ihr Lehrer wahrscheinlich will:

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.

Beste Übung

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.

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, 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. 

188
Aaron Hall

Schnelle Antwort (TL; DR)

Beispiel

### example01 -------------------
mystring  =   'coup_ate_grouping'
backwards =   mystring[::-1]
print backwards

### ... or even ...
mystring  =   'coup_ate_grouping'[::-1]
print mystring

### result01 -------------------
'''
gnipuorg_eta_puoc
'''

Ausführliche Antwort

Hintergrund

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 ()?

Problem

  • Kontext
    • Python 2.x
    • Python 3.x
  • Szenario:
    • Der Entwickler möchte eine Zeichenfolge umwandeln
    • Die Transformation soll die Reihenfolge aller Zeichen umkehren

Lösung

Fallstricke

  • Der Entwickler erwartet möglicherweise etwas wie string.reverse()
  • Die native idiomatic-Lösung (auch als " Pythonic ") - Lösung ist möglicherweise für neuere Entwickler nicht lesbar
  • Der Entwickler kann versucht sein, seine eigene Version von string.reverse() zu implementieren, um die Slice-Notation zu vermeiden.
  • Die Ausgabe der Slice-Notation kann in einigen Fällen kontraintuitiv sein:
    • siehe beispielsweise Beispiel 02
      • print 'coup_ate_grouping'[-4:] ## => 'ping'
      • verglichen mit
      • print 'coup_ate_grouping'[-4:-1] ## => 'pin'
      • verglichen mit
      • print 'coup_ate_grouping'[-1] ## => 'g'
    • die unterschiedlichen Ergebnisse der Indizierung von [-1] können einige Entwickler abschrecken

Begründung

In 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.

Beispiel02

### 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'

Fazit

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.

Siehe auch

34
dreftymac

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."

10
pX0r

mit Slice-Notation

def rev_string(s): 
    return s[::-1]

mit der Funktion reversed ()

def rev_string(s): 
    return ''.join(reversed(s))

rekursion verwenden

def rev_string(s): 
    if len(s) == 1:
        return s

    return s[-1] + rev_string(s[:-1])
6
harry

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
5
akshaynagpal
def reverse(input):
    return reduce(lambda x,y : y+x, input)
3
Javier

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'
3
mdn

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:

 enter image description here 

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

 enter image description here 

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).

String-Umkehrung

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

  • U: Finger zeigt nach oben
  • M: brauner Modifikator
  • L: Finger zeigt nach links

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

Der Code

#!/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()
2
Martin Thoma

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!")
1
buzhidao

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
1
Alex

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.

1
Nitin Khanna
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))
1
JEX

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"))
0
Ehsan Ahmadi

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
0
matt

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. 

0
Claudiu Creanga

Weiß nicht über die Effizienz, aber eine kurze Lösung, die in den MIT Vorlesungen zu finden ist

s = "abcd"

s[-1:-(len(s)+1):-1]
Out[5]: 'dcba'

Sie können mehr über Slices lesen, um diesen Code besser zu verstehen.

0
Krishnadas PC

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
0
Hardik Patel