Обратить строку в Python

Нет встроенной функции reverse для объекта Python str. Каков наилучший способ реализации этого метода?

Если вы предоставите очень краткий ответ, пожалуйста, уточните его эффективность. Например, преобразуется ли объект str в другой объект и т.д.

Ответы

Ответ 1

Как насчет:

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

Это расширенный срез. Он работает, выполняя [begin:end:step] - оставляя начало и конец и задавая шаг -1, он меняет строку.

Ответ 2

@Paolo s[::-1] является самым быстрым; более медленный подход (возможно, более читаемый, но спорный) ''.join(reversed(s)).

Ответ 3

Каков наилучший способ реализации обратной функции для строк?

Мой собственный опыт с этим вопросом является академическим. Однако, если вы ищете быстрый ответ, используйте срез, который выполняет -1:

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

или более читаемо (но медленнее из-за поиска имени метода и того факта, что объединение формирует список при задании итератора), str.join:

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

или для удобства чтения и повторного использования, поместите срез в функцию

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

а затем:

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

Более длинное объяснение

Если вы заинтересованы в академической экспозиции, продолжайте читать.

В объекте Python str нет встроенной функции обратного преобразования.

Вот несколько вещей о строках Python, которые вы должны знать:

  • В Python строки неизменяемы. Изменение строки не изменяет строку. Он создает новый.

  • Строки разрезаны. Нарезка строки дает вам новую строку из одной точки строки, назад или вперед, в другую точку с помощью заданных приращений. Они принимают нотацию фрагмента или объект среза в индексе:

    string[subscript]
    

Нижний индекс создает срез, включая двоеточие внутри фигурных скобок:

    string[start:stop:step]

Чтобы создать срез за пределами фигурных скобок, вам нужно создать объект среза:

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

Доступный для чтения подход:

В то время как ''.join(reversed('foo')) является читаемым, он требует вызова метода string, str.join, на другую вызываемую функцию, которая может быть относительно медленной. Положим это на функцию - мы вернемся к ней:

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

Самый эффективный подход:

Гораздо быстрее используется обратный срез:

'foo'[::-1]

Но как мы можем сделать это более понятным и понятным для кого-то, менее знакомого с фрагментами или намерениями оригинального автора? Позвольте создать объект среза за пределами нотации индекса, дать ему описательное имя и передать его в нотацию подстроки.

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

Выполнять как функцию

Чтобы реализовать это как функцию, я думаю, что это семантически ясно, чтобы просто использовать описательное имя:

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

И использование просто:

reversed_string('foo')

Что, возможно, хочет ваш учитель:

Если у вас есть инструктор, они, вероятно, хотят, чтобы вы начали с пустой строки и создали новую строку из старой. Вы можете сделать это с помощью чистого синтаксиса и литералов, используя цикл 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

Это теоретически плохо, потому что, помните, строки неизменяемы - поэтому каждый раз, когда вы добавляете символ на свой new_string, он теоретически создает новую строку каждый раз! Однако CPython знает, как оптимизировать это в некоторых случаях, из которых этот тривиальный случай один.

Лучшая практика

Теоретически лучше собрать свои подстроки в списке и присоединиться к ним позже:

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)

Однако, как мы увидим в таймингах ниже для CPython, это на самом деле занимает больше времени, потому что CPython может оптимизировать конкатенацию строк.

Задержки

Ниже приведены тайминги:

>>> 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 оптимизирует конкатенацию строк, тогда как другие реализации не могут:

... не полагаются на эффективную реализацию CPython встроенной конкатенации строк для операторов в форме a + = b или = a + b. Эта оптимизация хрупкая даже в CPython (она работает только для некоторых типов) и вообще отсутствует в реализациях, которые не используют refcounting. В чувствительных к производительности частях библиотеки вместо этого следует использовать форму .join(). Это гарантирует, что конкатенация происходит в линейном времени по различным реализациям.

Ответ 4

Быстрый ответ (TL; DR)

пример

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

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

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

Подробный ответ

Фон

Этот ответ предоставлен для решения следующих проблем @odigity:

Вот это да. Сначала я пришел в ужас от решения, предложенного Паоло, но оно отошло на второй план к ужасу, который я почувствовал, прочитав первый комментарий: "Это очень питонично. Хорошая работа!" Я так обеспокоен, что такое яркое сообщество думает, что использование таких загадочных методов для чего-то такого базового - хорошая идея. Почему это не просто s.reverse()?

проблема

  • контекст
    • Python 2.x
    • Python 3.x
  • Сценарий:
    • Разработчик хочет преобразовать строку
    • Преобразование в обратном порядке всех символов

Решение

Ловушки

  • Разработчик может ожидать что-то вроде string.reverse()
  • Нативное идиоматическое (иначе называемое " pythonic ") решение может быть нечитаемым для новых разработчиков
  • Разработчик может испытать желание реализовать свою собственную версию string.reverse() чтобы избежать записи срезов.
  • Вывод записи среза может быть нелогичным в некоторых случаях:
    • см. например, example02
      • print 'coup_ate_grouping'[-4:] ## => 'ping'
      • по сравнению с
      • print 'coup_ate_grouping'[-4:-1] ## => 'pin'
      • по сравнению с
      • print 'coup_ate_grouping'[-1] ## => 'g'
    • различные результаты индексации на [-1] могут [-1] некоторых разработчиков

обоснование

У Python есть особое обстоятельство, о котором следует знать: строка является итеративным типом.

Одно из оснований для исключения метода string.reverse() состоит в том, чтобы дать разработчикам Python стимул использовать возможности этого особого обстоятельства.

Проще говоря, это просто означает, что с каждым отдельным символом в строке можно легко работать как часть последовательного расположения элементов, как массивы в других языках программирования.

Чтобы понять, как это работает, обзор example02 может дать хороший обзор.

Example02

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

Заключение

Когнитивная нагрузка, связанная с пониманием того, как работает нотация слайсов в Python, действительно может быть слишком большой для некоторых пользователей и разработчиков, которые не хотят тратить много времени на изучение языка.

Тем не менее, как только основные принципы будут поняты, сила этого подхода по сравнению с методами манипуляции с фиксированной струной может быть весьма благоприятной.

Для тех, кто думает иначе, существуют альтернативные подходы, такие как лямбда-функции, итераторы или простые одноразовые объявления функций.

При желании разработчик может реализовать свой собственный метод string.reverse(), однако полезно понять обоснование этого аспекта python.

Смотрите также

Ответ 5

Менее сложным способом взглянуть на это будет:

string = 'happy'
print(string)

'счастливый'

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

'Yppah'

На английском языке [-1:: - 1] читается как:

"Начиная с -1, пройдите полностью, выполнив шаги -1"

Ответ 6

1. используя обозначение среза

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

2. используя функцию reversed()

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

3. с помощью рекурсии

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

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

Ответ 7

Существующие ответы верны, только если игнорируются модификаторы Unicode/кластеры графем. Я расскажу об этом позже, но сначала посмотрим на скорость некоторых алгоритмов разворота:

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

Вы можете видеть, что время для понимания списка (reversed = string[::-1]) во всех случаях является самым низким (даже после исправления моей опечатки).

Перестановка строк

Если вы действительно хотите перевернуть строку в обычном смысле этого слова, это НАМНОГО сложнее. Например, возьмем следующую строку (коричневый палец, указывающий влево, желтый палец, указывающий вверх). Это две графемы, но три юникодных кода. Дополнительным является модификатор скина.

example = "👈🏾👆"

Но если вы измените его любым из указанных способов, вы получите коричневый палец, указывающий вверх, желтый палец, указывающий влево. Причина этого заключается в том, что "коричневый" модификатор цвета все еще находится в середине и применяется ко всему, что находится до него. Итак, у нас есть

  • U: палец направлен вверх
  • М: коричневый модификатор
  • L: палец, указывающий влево

и

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

Графические кластеры Unicode немного сложнее, чем просто кодовые точки модификатора. К счастью, есть библиотека для обработки графем:

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

и, следовательно, правильный ответ будет

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

который также является самым медленным:

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 __name__ == '__main__':
    main()

Ответ 8

Обрати строку в python без использования reverse() или [:: - 1]

def reverse(test):
    n = len(test)
    x=""
    for i in range(n-1,-1,-1):
        x += test[i]
    return x

Ответ 9

Это тоже интересный способ:

def reverse_words_1(s):
    rev = ''
    for i in range(len(s)):
        j = ~i  # equivalent to j = -(i + 1)
        rev += s[j]
    return rev

или похожие:

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

Еще один "экзотический" способ с использованием byterarray, который поддерживает .reverse()

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

будет производить:

'!siht esreveR'

Ответ 10

def reverse(input):
    return reduce(lambda x,y : y+x, input)

Ответ 11

original = "string"

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

print(original)
print(rev_index)
print(''.join(rev_func))

Ответ 12

Еще одна альтернатива (неэффективно! просто показать разнообразие Python с таким количеством возможных решений!): преобразовать строку в список, используя функцию list(). Значение списка - это изменяемый тип данных. Поэтому мы можем использовать метод reverse(), который меняет объекты списка на месте. Затем мы преобразуем список обратно в строку, используя метод объединения списка с пустым разделителем:

>>> s = 'hello world'
>>> s
'hello world'
>>> t = list(s) # convert to list
>>> t
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
>>> t.reverse() # reverse method of list
>>> t
['d', 'l', 'r', 'o', 'w', ' ', 'o', 'l', 'l', 'e', 'h']
>>> s = ''.join(t) # convert to string
>>> s
'dlrow olleh'

Ответ 13

Здесь нет ничего необычного:

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

Ответ 14

Рекурсивный метод:

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

Пример:

print(reverse("Hello!"))    #!olleH

Ответ 15

Все вышеперечисленные решения идеальны, но если мы пытаемся изменить строку, используя для цикла в python, будет немного сложно, вот как мы можем изменить строку, используя для цикла

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

Я надеюсь, что этот будет полезен для кого-то.

Ответ 16

Это мой путь:

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

Ответ 17

Вот один без [::-1] или reversed (для учебных целей):

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

вы можете использовать += для конкатенации строк, но join() выполняется быстрее.

Ответ 18

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"

Это работает путем циклического перебора строки и присвоения ее значений в обратном порядке другой строке.

Ответ 19

Есть много способов перевернуть строку, но я также создал другую просто для удовольствия. Я думаю, что такой подход не так уж и плох.

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

Ответ 20

Этот класс использует магические функции Python для обращения строки:

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)  

Выход

dlrow olleh

Ссылка

Ответ 21

Вот просто:

print "loremipsum" [- 1:: - 1]

и некоторые логически:

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

выход:

muspimerol

Ответ 22

Это простая и значимая обратная функция, легкая для понимания и кодирования

def reverse_sentence(text):
    words = text.split(" ")
    reverse =""
    for word in reversed(words):
        reverse += word+ " "
    return reverse

Ответ 23

s = 'Hello world'

s [:: - 1]

в приведенном выше примере метка s или переменная s содержит строку, содержащую строку Hello world, и на втором этапе я печатаю обратную строку Hello world, переходя от всего ко всему в обратном шаговом порядке с -1.

Ответ 24

Переверните строку без магии питона.

>>> def reversest(st):
    a=len(st)-1
    for i in st:
        print(st[a],end="")
        a=a-1

Ответ 25

Конечно, в Python вы можете очень притворяться 1-строчным материалом.:)
Здесь представлено простое и универсальное решение, которое может работать на любом языке программирования.

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)

Ответ 26

s = 'hello'
ln = len(s)
i = 1
while True:
    rev = s[ln-i]
    print rev,
    i = i + 1
    if i == ln + 1 :
        break

ВЫХОД:

o l l e h

Ответ 27

Вы можете использовать обратную функцию со списком comprehesive. Но я не понимаю, почему этот метод был исключен в python 3, был излишне.

string = [ char for char in reversed(string)]