Зворотний рядок в Python


1348

Для об’єкта reversePython немає вбудованої функції str. Який найкращий спосіб реалізації цього методу?

Якщо ви даєте дуже коротку відповідь, будь ласка, детальніше розкажіть про її ефективність. Наприклад, чи strперетворюється об’єкт на інший об’єкт тощо.


1
використовуйте рядок "tacocat"
JonPizza

Відповіді:


2649

Як щодо:

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

Це синтаксис розширеного фрагмента . Він працює, виконуючи виконання [begin:end:step]- залишаючи початок і кінець і вказуючи крок -1, він обертає рядок.


28
Це не працює для utf8, хоча .. Мені потрібно було це зробити також, b = a.decode('utf8')[::-1].encode('utf8')але дякую за правильний напрямок!
Рікі Леві

13
@RickyLevi Якщо .decode('utf8')потрібно, це означає, aщо не містить жодних рядкових об'єктів, а більше байтів.
Шиплу Мокаддім

255

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


13
Це приблизно в 3 рази повільніше.
себе

2
І швидкий коментар, щоб сказати, що це робить, пояснить це краще, ніж використання цієї повільнішої версії!
грози13

4
це повільніше , тому що join є для створення списку в будь-якому випадку , щоб мати можливість отримати розмір. ''.join(list(reversed(s)))може бути трохи швидше.
Жан-Франсуа Фабре

Чи є у вас інформація про те, чому [:: - 1] найшвидший? Я хотів би зануритися глибше.
Таннер

@Tanner [:: - 1] найшвидший, тому що не викликає жодних зовнішніх функцій, скоріше, він використовує нарізку, яка оптимізована в python. '' .join (список (зворотний (и))) робить 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): . Найбільше мені подобається, що обраний рядок, який ви вибрали, - це той випадок, коли вам ніколи не потрібно було би перевертати його, і не змогли б сказати, чи мали би ви це :)
Давос,

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()
  • Народне ідіоматичне (також " пітонічне ") рішення може бути не читабельним для нових розробників
  • Розробник може спокуситися реалізувати свою власну версію, string.reverse()щоб уникнути позначення фрагментів.
  • Вихід нотації зрізів в деяких випадках може бути протиінтуїтивним:
    • див., наприклад, приклад02
      • print 'coup_ate_grouping'[-4:] ## => 'ping'
      • у порівнянні з
      • print 'coup_ate_grouping'[-4:-1] ## => 'pin'
      • у порівнянні з
      • print 'coup_ate_grouping'[-1] ## => 'g'
    • різні результати індексації [-1]можуть відхилити деяких розробників

Обґрунтування

У Python слід пам'ятати особливу обставину: рядок є ітерабельним типом.

Одне обґрунтування виключення string.reverse()методу - надати розробникам пітонів стимул використовувати силу цієї особливої ​​обставини.

Якщо говорити спрощено, це просто означає, що кожним окремим символом у рядку можна легко керувати, як частиною послідовного розташування елементів, як і масиви інших мов програмування.

Щоб зрозуміти, як це працює, перегляд прикладу02 може дати хороший огляд.

Приклад02

### 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 Modifiers / Grapheme ігноруються. Я з цим розберуся пізніше, але спочатку погляньте на швидкість деяких алгоритмів розвороту:

введіть тут опис зображення

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 кодові точки Unicode. Додатковий - це модифікатор шкіри .

example = "👈🏾👆"

Але якщо повернути його будь-яким із заданих способів, ви отримаєте коричневий палець вгору , жовтий палець вліво . Причиною цього є те, що модифікатор "коричневого" кольору все ще знаходиться посередині і застосовується до того, що знаходиться перед ним. Так ми маємо

  • U: палець вгору
  • М: коричневий модифікатор
  • L: вказівний палець ліворуч

і

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

Кластери Unicode Grapheme трохи складніше, ніж просто модифікаційні кодові точки. На щастя, є бібліотека для обробки графем :

>>> 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. за допомогою функції зворотного ()

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
Я натиснув нагороду, тому що мені подобається цей лямбдаський вираз. На жаль, це найменш ефективне рішення з усіх перерахованих вище (тест: Gist palindrome.py )
oski86

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

Всі вищезазначені рішення є ідеальними, але якщо ми намагаємося змінити рядок за допомогою циклу в python, це стане дещо хитро, тому ось як ми можемо повернути рядок за допомогою циклу

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

Довідково


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

вихід:

муспімерол



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

Питання задає зворотний рядок, а ви натомість надаєте список ??
користувач21820

вам потрібно .joinщось або щось, щоб зробити його правильною відповіддю
AsheKetchum

1
Дорога, [c for c in string]рівнозначна list(string).
Права нога
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.