Перевернуть строку в Python - PullRequest
1234 голосов
/ 31 мая 2009

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

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

Ответы [ 25 ]

2490 голосов
/ 31 мая 2009

Как насчет:

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

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

242 голосов
/ 31 мая 2009

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

195 голосов
/ 08 января 2015

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

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

36 голосов
/ 01 ноября 2015

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

Wow. Сначала я пришел в ужас от решения, предложенного Паоло, но это отошел на второй план к ужасу, который я почувствовал, прочитав первый комментарий: "Это очень питонично. Хорошая работа!" Я так обеспокоен, что такое светлое сообщество думает, используя такие загадочные методы для чего-то так Основное это хорошая идея. Почему это не просто 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.

Смотри также

10 голосов
/ 01 апреля 2016

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

string = 'happy'
print(string)

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

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

Yppah '

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

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

8 голосов
/ 21 мая 2018

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])
5 голосов
/ 25 сентября 2018

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

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'
5 голосов
/ 10 декабря 2014

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

def reverse(test):
    n = len(test)
    x=""
    for i in range(n-1,-1,-1):
        x += test[i]
    return x
3 голосов
/ 29 декабря 2018
original = "string"

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

print(original)
print(rev_index)
print(''.join(rev_func))
3 голосов
/ 26 июня 2015
def reverse(input):
    return reduce(lambda x,y : y+x, input)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...