Перевернуть строку в Python

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

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

41 ответ

Решение

Как насчет:

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

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

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

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

Мой собственный опыт в этом вопросе академический. Однако, если вы профессионал, ищущий быстрый ответ, используйте фрагмент, который шаг за шагом -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'

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

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

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

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

  1. В Python строки являются неизменяемыми. Изменение строки не изменяет строку. Это создает новый.

  2. Строки срезаемые. Нарезка строки дает вам новую строку из одной точки в строке, назад или вперед, в другую точку с заданными приращениями. Они принимают обозначение среза или объект среза в нижнем индексе:

    string[subscript]
    

Подстрочный индекс создает срез путем включения двоеточия в фигурные скобки:

    string[start:stop:step]

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

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

Читаемый подход:

В то время как ''.join(reversed('foo')) читается, требует вызова строкового метода, 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 = a + b . Эта оптимизация хрупка даже в CPython (она работает только для некоторых типов) и совсем не присутствует в реализациях, которые не используют пересчет. В чувствительных к производительности частях библиотеки следует использовать форму ''.join(). Это будет гарантировать, что объединение происходит в линейное время в разных реализациях.

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

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

Вы можете видеть, что время для понимания списка (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()

Быстрый ответ (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] может скинуть некоторых разработчиков

обоснование

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

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

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

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

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

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

Менее озадачивающий способ посмотреть на это будет:

string = 'happy'
print(string)

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

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

'Yppah'

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

"Начиная с -1, пройти весь путь, принимая шаги -1"

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

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

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

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 = byterarray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')`

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

'!siht esreveR'
def reverse(input):
    return reduce(lambda x,y : y+x, input)

В Python есть несколько способов перевернуть строку

Метод нарезки

      string = "python"
rev_string = string[::-1]
print(rev_string)

используя обратную функцию

      string = "python"
rev= reversed(string) 
rev_string = "".join(rev) 
print(rev_string)

Использование рекурсии

      string = "python"
def reverse(string):
  if len(string)==0:
    return string
  else:
    return reverse(string[1:])+string[0]
print(reverse(string))

Использование для цикла

      string = "python"
rev_string =""
for s in string:
  rev_string = s+ rev_string
print(rev_string)

Использование цикла while

      string = "python"
rev_str =""
length = len(string)-1
while length >=0:
  rev_str += string[length]
  length -= 1
print(rev_str)

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

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

Другая альтернатива (неэффективно! Просто чтобы показать разнообразие 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'

Решить это программным способом для собеседования

def reverse_a_string(string: str) -> str:
    """
    This method is used to reverse a string.
    Args:
        string: a string to reverse

    Returns: a reversed string
    """
    if type(string) != str:
        raise TypeError("{0} This not a string, Please provide a string!".format(type(string)))
    string_place_holder = ""
    start = 0
    end = len(string) - 1
    if end >= 1:
        while start <= end:
            string_place_holder = string_place_holder + string[end]
            end -= 1
        return string_place_holder
    else:
        return string


a = "hello world"
rev = reverse_a_string(a)
print(rev)

Выход:

dlrow olleh
original = "string"

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

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

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

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

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

 a=input()
 print(a[::-1])

Приведенный выше код получает ввод от пользователя и выводит результат, равный обратной стороне ввода, добавляя [::-1].

ВЫХОД:

>>> Happy 
>>> yppaH

Но когда дело доходит до предложений, просмотрите вывод кода ниже:

>>> Have a happy day
>>> yad yppah a evaH

Но если вы хотите, чтобы поменяли местами только символы строки, а не последовательность строки, попробуйте следующее:

a=input().split() #Splits the input on the basis of space (" ")
for b in a: #declares that var (b) is any value in the list (a)
    print(b[::-1], end=" ") #End declares to print the character in its quotes (" ") without a new line.

В приведенном выше коде в строке 2 я сказал, что ** переменная b - это любое значение в списке (a)** Я сказал, что var a является списком, потому что, когда вы используете разделение во входных данных, переменная входа становится списком. Также помните, что split нельзя использовать в случае int(input())

ВЫХОД:

>>> Have a happy day
>>> evaH a yppah yad

Если мы не добавим конец (" ") в приведенный выше код, он будет напечатан следующим образом:

>>> Have a happy day
>>> evaH
>>> a
>>> yppah
>>> yad

Ниже приведен пример понимания функции end():

КОД:

for i in range(1,6):
     print(i) #Without end()

ВЫХОД:

>>> 1
>>> 2
>>> 3
>>> 4
>>> 5

Теперь кодируйте с помощью end():

for i in range(1,6):
    print(i, end=" || ")

ВЫХОД:

>>> 1 || 2 || 3 || 4 || 5 ||

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

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

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

Способ # 1

      k = 'Hello world!'
name = ''

for i in k:
    name = f'{i}{name}'
    # name = i + name

print(name)

Способ # 2

      k = 'Hello world!'[::-1]
print(k)

Способ # 3

      k = 'Hello world!'
print(''.join(reversed(k)))

Метод # 4

      k = 'Hello world!'

output = []

length = len(k)-1

for i in range(len(k)):
    while(length != -1):
        output.append(k[length])
        length -= 1

print(''.join(output))

Метод # 5

      k = 'Hello world!'

output = []

for i in range(len(k)-1, -1, -1):
    output.append(k[i])

print(''.join(output))

my_str = "hello

Быстрый вариант: my_str[::-1]

Медленный вариант: "".join(reversed(my_str))

Не знаю об эффективности, но короткое решение, найденное на лекциях MIT

s = "abcd"

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

Вы можете прочитать больше о ломтиках, чтобы лучше понять этот код.

Это мой путь:

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] или же 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() быстрее.

Использование цикла For

      name = 'Python'

strnew = ""
for i in name:
    strnew = i+strnew
print("After reverse string " +strnew)
def rev(str1):


    print( str1[::-1])

str1="123"
r=rev(str1)

Этот класс использует магические функции 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

Ссылка

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

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

Просто как другое решение (потому что его спрашивают в интервью):

      def reverse_checker(string):
    ns = ""
    for h in range(1,len(string)+1):
        ns  += string[-h]
    print(ns)

    if ns == string:
        return True
    else:
        return False
      #Method 1:
word = input("Enter a word: ")
newword = "".join(reversed(word))
print(newword)

#Method 2:
word = input("Enter a word: ")
newword = word[::-1]
print(newword)

#Method 3:
word = input("Enter a word: ")
newword  = ''
for i in word:
    newword = i + newword
print(newword)

#Method 4:
word = input("Enter a word: ")
newword  = ''
for i in range(len(word)-1 , -1 , -1):
    newword =  newword + word[i]
print(newword)
Другие вопросы по тегам