Inverter uma string em Python

1351

Não há reversefunção integrada para o strobjeto do Python . Qual é a melhor maneira de implementar esse método?

Se você fornecer uma resposta muito concisa, descreva sua eficiência. Por exemplo, se o strobjeto é convertido em um objeto diferente etc.

a si mesmo
fonte
1
use a string "tacocat"
JonPizza 15/12/19

Respostas:

2653

E se:

>>> 'hello world'[::-1]
'dlrow olleh'

Essa é a sintaxe de fatia estendida . Isso funciona [begin:end:step]- deixando start e end off e especificando uma etapa de -1, ela reverte uma string.

Paolo Bergantino
fonte
29
Mas isso não funciona para o utf8. Eu precisava fazer isso também, b = a.decode('utf8')[::-1].encode('utf8')mas obrigado pela direção certa!
21717 Ricky Levi
14
@RickyLevi Se .decode('utf8')for necessário, significa aque não contém nenhum objeto de string, e sim bytes.
Shiplu Mokaddim 1/11
256

@ Paolo s[::-1]é o mais rápido; é uma abordagem mais lenta (talvez mais legível, mas discutível) ''.join(reversed(s)).

Alex Martelli
fonte
14
Isso é cerca de 3 vezes mais lento.
se
2
E um comentário rápido para dizer o que faz explicará melhor do que usar esta versão mais lenta!
precisa saber é o seguinte
4
é mais lento porque join tem que criar a lista de qualquer maneira para poder obter o tamanho. ''.join(list(reversed(s)))pode ser um pouco mais rápido.
Jean-François Fabre
Você tem alguma informação sobre por que [:: - 1] é mais rápido? Eu gostaria de mergulhar mais fundo.
Tanner #
O @Tanner [:: - 1] é o mais rápido porque não chama nenhuma função externa, mas sim o slicing, que é altamente otimizado em python. '' .join (lista (invertida (s))) faz três chamadas de função.
hd1 27/04
217

Qual é a melhor maneira de implementar uma função reversa para strings?

Minha própria experiência com essa questão é acadêmica. No entanto, se você é um profissional que procura a resposta rápida, use uma fatia que segue -1:

>>> 'a string'[::-1]
'gnirts a'

ou mais legível (mas mais lento devido às pesquisas de nome método e o fato de que se juntam formas uma lista quando dado um iterator), str.join:

>>> ''.join(reversed('a string'))
'gnirts a'

ou para legibilidade e reutilização, coloque a fatia em uma função

def reversed_string(a_string):
    return a_string[::-1]

e depois:

>>> reversed_string('a_string')
'gnirts_a'

Explicação mais longa

Se você estiver interessado na exposição acadêmica, continue lendo.

Não há função reversa interna no objeto str do Python.

Aqui estão algumas coisas sobre as strings do Python que você deve saber:

  1. No Python, as strings são imutáveis . Alterar uma sequência não modifica a sequência. Cria um novo.

  2. Strings são cortáveis. Cortar uma corda fornece uma nova corda de um ponto na corda, para trás ou para a frente, para outro ponto, por determinados incrementos. Eles recebem notação de fatia ou um objeto de fatia em um subscrito:

    string[subscript]

O subscrito cria uma fatia incluindo dois pontos nos chavetas:

    string[start:stop:step]

Para criar uma fatia fora dos aparelhos, você precisará criar um objeto de fatia:

    slice_obj = slice(start, stop, step)
    string[slice_obj]

Uma abordagem legível:

Embora ''.join(reversed('foo'))seja legível, é necessário chamar um método string str.join, em outra função chamada, que pode ser relativamente lenta. Vamos colocar isso em uma função - retornaremos a ela:

def reverse_string_readable_answer(string):
    return ''.join(reversed(string))

Abordagem de melhor desempenho:

Muito mais rápido é usar uma fatia reversa:

'foo'[::-1]

Mas como podemos tornar isso mais legível e compreensível para alguém menos familiarizado com fatias ou com a intenção do autor original? Vamos criar um objeto de fatia fora da notação subscrita, dar um nome descritivo e passá-lo para a notação subscrita.

start = stop = None
step = -1
reverse_slice = slice(start, stop, step)
'foo'[reverse_slice]

Implementar como função

Para realmente implementar isso como uma função, acho que é semanticamente claro o suficiente para simplesmente usar um nome descritivo:

def reversed_string(a_string):
    return a_string[::-1]

E o uso é simplesmente:

reversed_string('foo')

O que seu professor provavelmente quer:

Se você tem um instrutor, eles provavelmente querem que você comece com uma string vazia e crie uma nova string a partir da antiga. Você pode fazer isso com sintaxe pura e literais usando um loop 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

Teoricamente, isso é ruim porque, lembre-se, as strings são imutáveis - então toda vez que parece que você está adicionando um personagem ao seu new_string, é teoricamente que você cria uma nova string toda vez! No entanto, o CPython sabe como otimizar isso em certos casos, dos quais esse caso trivial é um deles.

Melhor prática

Teoricamente melhor é coletar suas substrings em uma lista e juntá-las mais tarde:

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)

No entanto, como veremos nos intervalos abaixo para CPython, isso realmente leva mais tempo, porque o CPython pode otimizar a concatenação de cadeias.

Horários

Aqui estão os horários:

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

O CPython otimiza a concatenação de cadeias, enquanto outras implementações podem não :

... não confie na implementação eficiente do CPython de concatenação de strings no local para instruções no formato a + = b ou a = a + b. Essa otimização é frágil mesmo no CPython (funciona apenas para alguns tipos) e não está presente em implementações que não usam refcounting. Em partes sensíveis ao desempenho da biblioteca, o formulário '' .join () deve ser usado. Isso garantirá que a concatenação ocorra em tempo linear em várias implementações.

Aaron Hall
fonte
5
Adoro esta resposta, explicações sobre otimizações, legibilidade versus otimização, dicas sobre o que o professor deseja. Eu não tenho certeza sobre a melhor parte prática com o whilee diminuindo o índice, embora talvez isso é menos legível: for i in range(len(a_string)-1, -1, -1): . Acima de tudo o amor I que a cadeia exemplo que você escolheu é o caso onde você nunca iria precisar revertê-la, e não seria capaz de dizer se você teve :)
Davos
37

Resposta Rápida (TL; DR)

Exemplo

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

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

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

Resposta detalhada

fundo

Esta resposta é fornecida para resolver a seguinte preocupação do @odigity:

Uau. A princípio, fiquei horrorizado com a solução que Paolo propôs, mas isso ficou em segundo plano com o horror que senti ao ler o primeiro comentário: "Isso é muito pythonic. Bom trabalho!" Estou tão perturbado que uma comunidade tão brilhante pensa que usar métodos tão enigmáticos para algo tão básico é uma boa idéia. Por que não é apenas s.reverse ()?

Problema

  • Contexto
    • Python 2.x
    • Python 3.x
  • Cenário:
    • Desenvolvedor quer transformar uma string
    • Transformação é inverter a ordem de todos os caracteres

Solução

Armadilhas

  • O desenvolvedor pode esperar algo como string.reverse()
  • A solução idiomática nativa (aka " pythonic ") pode não ser legível para desenvolvedores mais recentes
  • O desenvolvedor pode ficar tentado a implementar sua própria versão string.reverse()para evitar a notação de fatia.
  • A saída da notação de fatia pode ser contra-intuitiva em alguns casos:
    • ver, por exemplo, example02
      • print 'coup_ate_grouping'[-4:] ## => 'ping'
      • comparado com
      • print 'coup_ate_grouping'[-4:-1] ## => 'pin'
      • comparado com
      • print 'coup_ate_grouping'[-1] ## => 'g'
    • os diferentes resultados da indexação [-1]podem afastar alguns desenvolvedores

Fundamentação

O Python tem uma circunstância especial a ser observada: uma string é um tipo iterável .

Uma justificativa para excluir um string.reverse()método é incentivar os desenvolvedores de python a aproveitar o poder dessa circunstância especial.

Em termos simplificados, isso significa simplesmente que cada caractere individual de uma string pode ser facilmente operado como parte de um arranjo seqüencial de elementos, assim como matrizes em outras linguagens de programação.

Para entender como isso funciona, revisar example02 pode fornecer uma boa visão geral.

Exemplo02

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

Conclusão

A carga cognitiva associada à compreensão de como a notação de fatia funciona em python pode realmente ser demais para alguns adotantes e desenvolvedores que não desejam investir muito tempo aprendendo a linguagem.

No entanto, uma vez entendidos os princípios básicos, o poder dessa abordagem sobre os métodos de manipulação de cordas fixas pode ser bastante favorável.

Para aqueles que pensam o contrário, existem abordagens alternativas, como funções lambda, iteradores ou declarações simples de funções pontuais.

Se desejado, um desenvolvedor pode implementar seu próprio método string.reverse (), no entanto, é bom entender a lógica por trás desse aspecto do python.

Veja também

dreftymac
fonte
17

As respostas existentes estão corretas apenas se os clusters Unicode Modifiers / grafema forem ignorados. Lidarei com isso mais tarde, mas primeiro observe a velocidade de alguns algoritmos de reversão:

insira a descrição da imagem aqui

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

insira a descrição da imagem aqui

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

Você pode ver que o tempo para a compreensão da lista ( reversed = string[::-1]) é em todos os casos de longe o mais baixo (mesmo depois de corrigir meu erro de digitação).

Reversão de String

Se você realmente deseja reverter uma string no senso comum, é MUITO mais complicado. Por exemplo, pegue a seguinte corda ( dedo marrom apontando para a esquerda , dedo amarelo apontando para cima ). Esses são dois grafemas, mas três pontos de código unicode. O adicional é um modificador de capa .

example = "👈🏾👆"

Mas se você o reverter com qualquer um dos métodos fornecidos, o dedo marrom será apontado para cima , o dedo amarelo apontado para a esquerda . A razão para isso é que o modificador de cor "marrom" ainda está no meio e é aplicado ao que estiver antes dele. Então nós temos

  • U: dedo apontando para cima
  • M: modificador marrom
  • L: dedo apontando para a esquerda

e

original: LMU
reversed: UML (above solutions)
reversed: ULM (correct reversal)

Os Clusters Unicode Grapheme são um pouco mais complicados do que apenas pontos de código modificadores. Felizmente, existe uma biblioteca para lidar com grafemas :

>>> import grapheme
>>> g = grapheme.graphemes("👈🏾👆")
>>> list(g)
['👈🏾', '👆']

e, portanto, a resposta correta seria

def reverse_graphemes(string):
    g = list(grapheme.graphemes(string))
    return ''.join(g[::-1])

que também é de longe o mais lento:

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

O código

#!/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 __name__ == '__main__':
    main()
Martin Thoma
fonte
10

1. usando notação de fatia

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

2. usando a função reversed ()

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

3. usando recursão

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

    return s[-1] + rev_string(s[:-1])
atormentar
fonte
1
É preciso observar a solução de recursão, se a string tiver um comprimento decente, você encontrará RecursionError: maximum recursion depth exceeded while calling a Python object. Ex:rev_string("abcdef"*1000)
Adam Parkin
9

Uma maneira menos desconcertante de ver isso seria:

string = 'happy'
print(string)

'feliz'

string_reversed = string[-1::-1]
print(string_reversed)

'yppah'

Em inglês [-1 :: - 1], lê-se como:

"Começando em -1, siga todo o caminho, executando etapas de -1"

pX0r
fonte
2
O -1ainda está desnecessários, no entanto.
Eric Duminil
7

Inverta uma string em python sem usar reversed () ou [:: - 1]

def reverse(test):
    n = len(test)
    x=""
    for i in range(n-1,-1,-1):
        x += test[i]
    return x
akshaynagpal
fonte
1
Você não deve usar xrange, pois não precisa da lista, no python 2?
UnitasBrooks
5

Esta também é uma maneira interessante:

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 similar:

def reverse_words_2(s):
    rev = ''
    for i in reversed(range(len(s)):
        rev += s[i]
    return rev

Outra maneira mais 'exótica' de usar byterarray que suporta .reverse ()

b = bytearray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')

vai produzir:

'!siht esreveR'
gxpr
fonte
3
def reverse(input):
    return reduce(lambda x,y : y+x, input)
Javier
fonte
3
Eu cliquei na votação, porque eu gosto dessa expressão lambda. Infelizmente, é a solução menos eficiente de todos os listados acima (teste: Síntese palindrome.py )
oski86
2
original = "string"

rev_index = original[::-1]
rev_func = list(reversed(list(original))) #nsfw

print(original)
print(rev_index)
print(''.join(rev_func))
JEX
fonte
1
Embora esse código possa responder à pergunta, é melhor explicar como resolver o problema e fornecer o código como exemplo ou referência. As respostas somente de código podem ser confusas e não têm contexto.
Robert Columbia
1
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"

Isso funciona fazendo um loop através de uma string e atribuindo seus valores na ordem inversa a outra string.

Pika, o Mago das Baleias
fonte
0

Aqui não é chique:

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!")
uma oferta não pode recusar
fonte
0

Aqui está um sem [::-1]ou reversed(para fins de aprendizado):

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

você pode usar +=para concatenar seqüências de caracteres, mas join()é mais rápido.

Claudiu Creanga
fonte
0

Método recursivo:

def reverse(s): return s[0] if len(s)==1 else s[len(s)-1] + reverse(s[0:len(s)-1])

exemplo:

print(reverse("Hello!"))    #!olleH
mate
fonte
0

Todas as soluções acima são perfeitas, mas se estivermos tentando reverter uma string usando o loop for em python se tornará um pouco complicado, então aqui está como podemos reverter uma string usando o loop for

string ="hello,world"
for i in range(-1,-len(string)-1,-1):
    print (string[i],end=(" ")) 

Espero que este seja útil para alguém.

Nitin Khanna
fonte
0

Esse é meu caminho:

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
Alex
fonte
0

Existem várias maneiras de reverter uma string, mas também criei outra apenas por diversão. Eu acho que essa abordagem não é tão ruim assim.

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

Esta classe usa funções mágicas python para reverter uma string:

class Reverse(object):
    """ Builds a reverse method using magic methods """

    def __init__(self, data):
        self.data = data
        self.index = len(data)


    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration

        self.index = self.index - 1
        return self.data[self.index]


REV_INSTANCE = Reverse('hello world')

iter(REV_INSTANCE)

rev_str = ''
for char in REV_INSTANCE:
    rev_str += char

print(rev_str)  

Resultado

dlrow olleh

Referência

Emma
fonte
-1

Com o python 3, você pode reverter a string no local, o que significa que ela não será atribuída a outra variável. Primeiro você precisa converter a string em uma lista e depois alavancar a reverse()função.

https://docs.python.org/3/tutorial/datastructures.html

   def main():
        my_string = ["h","e","l","l","o"]
        print(reverseString(my_string))

    def reverseString(s):
      print(s)
      s.reverse()
      return s

    if __name__ == "__main__":
        main()
Jonathan Kittell
fonte
-2

Esta é uma função reversa simples e significativa, fácil de entender e codificar

def reverse_sentence(text):
    words = text.split(" ")
    reverse =""
    for word in reversed(words):
        reverse += word+ " "
    return reverse
Kiran Sk
fonte
Embora isso possa responder à pergunta dos autores, faltam algumas palavras explicativas e / ou links para a documentação. Os trechos de código bruto não são muito úteis sem algumas frases ao seu redor. Você também pode descobrir como escrever uma boa resposta muito útil. Edite sua resposta.
Hellow
-3

Aqui está simplesmente:

imprimir "loremipsum" [- 1 :: - 1]

e alguns logicamente:

def str_reverse_fun():
    empty_list = []
    new_str = 'loremipsum'
    index = len(new_str)
    while index:
        index = index - 1
        empty_list.append(new_str[index])
    return ''.join(empty_list)
print str_reverse_fun()

resultado:

muspimerol

Chowdeswara Rao
fonte
-4

Inverta uma string sem magia python.

>>> def reversest(st):
    a=len(st)-1
    for i in st:
        print(st[a],end="")
        a=a-1
Hardik Patel
fonte
Imprimindo uma corda no sentido inverso é diferente algo que inverter uma string
Martin Thoma
-5

Claro, em Python você pode fazer coisas de uma linha muito chiques. :)
Aqui está uma solução simples e completa que pode funcionar em qualquer linguagem de programação.

def reverse_string(phrase):
    reversed = ""
    length = len(phrase)
    for i in range(length):
        reversed += phrase[length-1-i]
    return reversed

phrase = raw_input("Provide a string: ")
print reverse_string(phrase)
olga
fonte
1
Não é uma boa solução ter um código tão longo para uma tarefa tão trivial.
Hunter_71
-5
s = 'hello'
ln = len(s)
i = 1
while True:
    rev = s[ln-i]
    print rev,
    i = i + 1
    if i == ln + 1 :
        break

RESULTADO :

o l l e h
sudistack
fonte
-7

Você pode usar a função invertida com uma lista abrangente. Mas não entendo por que esse método foi eliminado no python 3, foi desnecessário.

string = [ char for char in reversed(string)]
alejandro izquierdo
fonte
A pergunta pede o inverso de uma string e você fornece uma lista?
user21820
você precisa de um .joinou algo para torná-lo uma resposta válida
AsheKetchum
1
BTW, [c for c in string]é o mesmo list(string).
Perna direita