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

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

Как перевернуть строку?

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

# У вас есть:
'TURBO'
# Нужно сделать из него:
'OBRUT'

Или может быть поставлена задача написать функцию, проверяющую, является ли заданная строка палиндромом. То есть, можно ли ее читать одинакового в правильном или противоположном порядке: 

def is_palindrome(string):

    reversed_string = string[::-1]

    return string == reversed_string

print(is_palindrome('TACOCAT')) # True

print(is_palindrome('TURBO')) # False

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

Объекты строк str в Python хранят не встроенный метод .reverse(), как можно предположить, если вы ранее работали с другими языками программирования (такими как Java или C#). То есть, такой подход будет неправильным: 

>>> 'TURBO'.reverse()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'reverse'

Сегодня рассмотрим три основных метода создание строки с противоположной последовательностью символов в Python.

Метод среза

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

Сюда входит отдельный случай, в котором срез последовательности “[::-1]” создает перевернутую копию. Так как строки Python являются последовательностями, это быстрый и простой способ получить отраженную копию строки: 

text = 'TURBO'[::-1]
print(text) # 'OBRUT'

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

def reverse_string1(s):
    return s[::-1]
print(reverse_string1('TURBO')) # 'OBRUT'

Как вам такой вариант? Вообще, это не только требует мало времени, но и достаточно удобно. Тем не менее существенный минус переворота строки с использованием среза в том, что он задействует продвинутую возможность Python, которая нередко девелоперами называется «тайной и древней». Ничего в этом удивительного нет, поскольку срез списка понять новичку не так и просто, если он раньше с этим не сталкивался. 

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

Самой существенной проблемой для многих является то, что синтаксис среза “[::-1]” не очень точно указывает на то, как он работает. Поэтому, в целом, этот вариант можно использовать. Правда, он создаст определенные трудности неподготовленному разработчику. 

Использование функций reversed() и str.join() для переворота строки

Есть еще один метод выполнения перевода строки с обратной итерацией. Для этого можно воспользоваться функцией reversed(), которая является встроенной в этом языке программирования. Вы получаете обратный итератор, применяемый для циклического перемещения объектов строки в противоположном направлении.

Работает этот код следующим образом: 

for elem in reversed('TURBO'):
    print(elem)

Результат:

O

B

R

U

T

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

Итак, все, что вы видел – это методы итерации над символами строки в обратном порядке. Но что делать, если нам таки надо внести изменения в оригинальную строку. Вообще, это сделать невозможно. Правда, есть альтернатива. Можно создать отраженную копию строки Python. Технически это делается следующим образом: 

text = ''.join(reversed('TURBO'))
print(text) # 'OBRUT'

Этот кусок кода содержит метод .join(), который все символы, которые были получены вследствие обратной итерации в новой строке, объединяет между собой. Классно, не так ли?

Естественно, у вас есть возможность повторно данный код извлечь в отдельную функцию, чтобы создать надлежащую функцию «перевернутой строки» в Python. Работает это следующим образом: 

def reverse_string2(s):
    return "".join(reversed(s)) 
data = reverse_string2('TURBO')
print(data) # 'OBRUT'

Такой подход обратного итератора для переворота строк в Python – это действительно удобный подход. Он четко говорит о том, что происходит. Даже новичок в этом языке программирования способен понять интуитивно, что создается перевернутая копия оригинальной строки.

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

Классический алгоритм переворота строк в Python

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

def reverse_string3(s):
    chars = list(s)
    for i in range(len(s) // 2):
        tmp = chars[i]
        chars[i] = chars[len(s) - i - 1]
        chars[len(s) - i - 1] = tmp
    return ''.join(chars)
data = reverse_string3('TURBO')
print(data) # 'OBRUT'

Как вы видите, это решение не то, чтобы родное, и не то, чтобы идиоматическое. Тут не задействуются возможности Python. И вообще, такое решение являет собой порт алгоритма, который есть в языке программирования C.

И вообще, это решение является самым не быстрым. В этом вы убедитесь в следующем разделе, где будет проводиться бенчмаркинг по всем трем реализациям.

Сравнение производительности

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

Для этого необходимо выполнить небольшой бенчмаркинг: 

>>> import timeit
>>> s = 'abcdefghijklmnopqrstuvwxyz' * 10
>>> timeit.repeat(lambda: reverse_string1(s))
[0.6848115339962533, 0.7366074129968183, 0.7358982900041156]
>>> timeit.repeat(lambda: reverse_string2(s))
[5.514941683999496, 5.339547180992668, 5.319950777004124]
>>> timeit.repeat(lambda: reverse_string3(s))
[48.74324739299482, 48.637329410004895, 49.223478018000606]

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

Теперь, смену символов на месте определенно можно оптимизировать. И благодаря тому, что было описано ранее, мы можем четко понимать, какой вариант является наиболее оптимальным.

Итог: переворачивание строк в Python

Таким образом, переворачивание строк – это стандартная операция для разработки любой программы, которая часто запрашивается в ходе собеседования о приеме на работу. Сегодня вы узнали о трех разных подходах к переворачиванию строк в Python.

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

Вариант 1: срез списка

Вы можете использовать синтаксис среза Python с целью развернуть строку. Точнее, создать копию имеющейся строки, в которой все символы будут расположены в противоположном направлении. Это прекрасно работает. Тем не менее синтаксис может быть недостаточно понятным интуитивно для новичков в этом языке программирования. Да и для опытных специалистов также это заставит сделать паузу во время чтения кода и вдуматься.

Но вот для компьютера этот вариант, наоборот, наиболее понятный. Поэтому программа, в которой он реализуется таким способом, будет работать максимально быстро. 

print('TURBO'[::-1]) # 'OBRUT'

Такой простой метод создает перевернутую копию строки и является наиболее быстрым методом выполнения переворота строки в этом языке программирования.

Вариант 2: использование функций reversed() и str.join()

Встроенная функция reversed() дает возможность создать отраженный итератор строки Python (либо какой-угодно другой последовательный объект). Это решение является гибким и простым в использовании. Оно задействует определенные продвинутые возможности Python.

Читаемость кода в этом случае не будет нарушена, поскольку название функции reversed() дает сразу возможность понять, что делает конкретный участок кода: 

print(''.join(reversed('TURBO'))) # 'OBRUT'

Функция reversed() возвращает итератор, который выполняет итерацию над символами в строке в противоположном порядке. Данный поток символов нужно комбинировать в строку, используя функции str.join(). Этот метод медленнее по сравнению со срезом, но гораздо более читаемый.

Вариант 3: «Крутите сами»

Стандартный алгоритм замены символов с предварительным портированием в Python также работает. Правда, этот способ непроизводительный и абсолютно нечитаемый по сравнению с другими методами: 

def reverse_string(s):
    chars = list(s)
    for i in range(len(s) // 2):
        tmp = chars[i]
        chars[i] = chars[len(s) - i - 1]
        chars[len(s) - i - 1] = tmp
    return ''.join(chars)
data = reverse_string('TURBO')
print(data) # 'OBRUT'

Этот метод не рекомендуется в практическом использовании. Но если о нем сказать во время собеседования, можно показать умение одну и ту же задачу решать разными способами.

Какой же вариант использовать?

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

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

Нам необходимо забыть о том, что существует несуществующая эффективность. Одним словом, оптимизация должна быть в тех ситуациях, когда от нее действительно есть толк. Как правило, в 97% случаях в ней нет никакой необходимости. Но и 3% – это не так уж и мало.

ОфисГуру
Adblock
detector