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


1351

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

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


1
используйте строку "Такокат"
JonPizza

Ответы:


2654

Как насчет:

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

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


29
Это не работает для utf8, хотя .. Мне нужно было сделать это, b = a.decode('utf8')[::-1].encode('utf8')но спасибо за правильное направление!
Рики Леви

14
@RickyLevi Если .decode('utf8')требуется, это означает, aчто не содержит никаких строковых объектов, скорее байтов.
Шиплу Мокаддим

256

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


14
Это примерно в 3 раза медленнее.
сам

2
И быстрый комментарий, чтобы сказать, что он делает, объяснит это лучше, чем использование этой более медленной версии!
tburrows13

4
это медленнее, потому join что в любом случае приходится составлять список, чтобы получить размер. ''.join(list(reversed(s)))может быть немного быстрее
Жан-Франсуа Фабр

У вас есть информация о том, почему [:: - 1] самый быстрый? Я хотел бы погрузиться глубже.
Таннер

@Tanner [:: - 1] является самым быстрым, потому что он не вызывает никаких внешних функций, скорее он использует нарезку, которая высоко оптимизирована в python. '' .join (list (полностью изменено)) делает 3 вызова функции.
hd1

217

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

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


5
Мне нравится этот ответ, объяснения об оптимизации, читабельность и оптимизация, советы о том, что хочет учитель. Я не уверен , о практике раздел с лучшими whileи декремента индекса, хотя , возможно , это менее читаемыми: for i in range(len(a_string)-1, -1, -1): . Больше всего мне нравится, что выбранная вами примерная строка - это тот случай, когда вам никогда не понадобится перевернуть ее, и вы не сможете сказать, что у вас
Davos

37

Быстрый ответ (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.

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


17

Существующие ответы верны только в том случае, если игнорируются модификаторы 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]) во всех случаях намного меньше (даже после исправления моей опечатки).

Обращение строки

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

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

10

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

1
Нужно посмотреть решение рекурсии, если вы наткнетесь на строку приличной длины RecursionError: maximum recursion depth exceeded while calling a Python object. Пример:rev_string("abcdef"*1000)
Адам Паркин

9

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

string = 'happy'
print(string)

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

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

'Yppah'

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

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


2
Тем -1не менее, все еще не требуется.
Эрик

7

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

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

1
Разве вы не должны использовать xrange, так как вам не нужен список в python 2?
UnitasBrooks

5

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

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'

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

3
Я нажал upvote, потому что мне нравится это лямбда-выражение. К сожалению, это наименее эффективное решение из всех вышеперечисленных (тест: Gist
palindrome.py

2
original = "string"

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

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

1
Хотя этот код может ответить на вопрос, лучше объяснить, как решить проблему, и предоставить код в качестве примера или ссылки. Ответы только на код могут быть запутанными и не хватает контекста.
Роберт Колумбия,

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"

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



0

Вот один без [::-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()это быстрее.


0

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

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

0

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

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

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


0

Это мой путь:

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

0

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

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

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

Ссылка


0

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

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

-1

В Python 3 вы можете перевернуть строку на месте, что означает, что она не будет присвоена другой переменной. Сначала вы должны преобразовать строку в список, а затем использовать reverse()функцию.

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

-2

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

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

Хотя это может ответить на вопрос авторов, в нем отсутствуют некоторые поясняющие слова и / или ссылки на документацию. Фрагменты исходного кода не очень полезны без каких-либо фраз вокруг них. Вы также можете найти, как написать хороший ответ очень полезно. Пожалуйста, отредактируйте свой ответ.
привет

-3

Вот просто:

печать "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


-4

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

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

Печать строки в обратном порядке - это нечто иное, чем перестановка строки
Мартин Тома,

-5

Конечно, в 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)

1
Это не очень хорошее решение иметь такой длинный код для такой тривиальной задачи.
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

ВЫВОД :

o l l e h

-7

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

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

Вопрос требует обратной строки, а вы вместо этого даете список ??
user21820

вам нужно .joinили что-то, чтобы сделать правильный ответ
AsheKetchum

1
Кстати, [c for c in string]это равнозначно list(string).
Правая нога
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.