用python反转字符串

Reverse a string in Python

python的str对象没有内置的reverse函数。实现此方法的最佳方法是什么?

如果提供非常简洁的答案,请详细说明其效率。例如,是否将str对象转换为其他对象等。


怎么样:

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

这是扩展切片语法。它的工作方式是执行[begin:end:step],将begin和end设为off,并指定步骤-1,从而反转字符串。


@Paolo的s[::-1]是最快的;''.join(reversed(s))是一种较慢的方法(可能更易读,但有争议)。


What is the best way of implementing a reverse function for strings?

我在这个问题上的经验是学术性的。但是,如果你是一个寻求快速答案的专家,那么就使用一个由-1步进的切片:

1
2
>>> 'a string'[::-1]
'gnirts a'

或者更易读(但由于方法名查找和在给定迭代器时联接形成列表的事实而变慢),str.join

1
2
>>> ''.join(reversed('a string'))
'gnirts a'

或者为了可读性和可重用性,将切片放入函数中

1
2
def reversed_string(a_string):
    return a_string[::-1]

然后:

1
2
>>> reversed_string('a_string')
'gnirts_a'

更长的解释

如果你对学术博览会感兴趣,请继续阅读。

There is no built-in reverse function in Python's str object.

关于Python的字符串,您应该知道以下几点:

  • 在Python中,字符串是不可变的。更改字符串不会修改字符串。它创造了一个新的。

  • 字符串是可切片的。对字符串进行切片将为您提供一个新的字符串,从字符串中的一个点向后或向前,以给定的增量到另一个点。它们采用切片表示法或下标中的切片对象:

    1
    string[subscript]
  • 下标通过在大括号中包含冒号来创建切片:

    1
        string[start:stop:step]

    要在大括号外创建一个切片,需要创建一个切片对象:

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

    一种可读的方法:

    虽然''.join(reversed('foo'))是可读的,但它需要在另一个被调用函数上调用一个字符串方法str.join,这可能会比较慢。让我们把它放到一个函数中-我们将回到它:

    1
    2
    def reverse_string_readable_answer(string):
        return ''.join(reversed(string))

    最有效的方法:

    使用反向切片要快得多:

    1
    'foo'[::-1]

    但是,我们如何才能让不太熟悉原作者意图的人更容易理解和理解这一点呢?让我们在下标符号之外创建一个slice对象,给它一个描述性名称,然后将它传递给下标符号。

    1
    2
    3
    4
    start = stop = None
    step = -1
    reverse_slice = slice(start, stop, step)
    'foo'[reverse_slice]

    作为功能实现

    要将其实际实现为一个函数,我认为它在语义上足够清晰,可以简单地使用描述性名称:

    1
    2
    def reversed_string(a_string):
        return a_string[::-1]

    使用方法很简单:

    1
    reversed_string('foo')

    你的老师可能想要:

    如果你有一个指导老师,他们可能希望你从一个空字符串开始,然后从旧的字符串开始构建一个新的字符串。您可以使用while循环使用纯语法和文本来执行此操作:

    1
    2
    3
    4
    5
    6
    7
    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知道如何在某些情况下对此进行优化,而这个微不足道的情况就是其中之一。

    最佳实践

    理论上更好的方法是将子字符串收集到一个列表中,然后再将它们连接起来:

    1
    2
    3
    4
    5
    6
    7
    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可以优化字符串连接。

    计时

    以下是时间安排:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> 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优化了字符串连接,而其他实现可能不会:

    ... do not rely on CPython's efficient implementation of in-place string concatenation for statements in the form a += b or a = a + b . This optimization is fragile even in CPython (it only works for some types) and isn't present at all in implementations that don't use refcounting. In performance sensitive parts of the library, the ''.join() form should be used instead. This will ensure that concatenation occurs in linear time across various implementations.


    快速回答(TL;DR)例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ### 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. I was horrified at first by the solution Paolo proposed, but that
    took a back seat to the horror I felt upon reading the first
    comment:"That's very pythonic. Good job!" I'm so disturbed that such
    a bright community thinks using such cryptic methods for something so
    basic is a good idea. Why isn't it just s.reverse()?

    问题

    • 语境
      • Python 2 x
      • Python 3 x
    • 脚本:
      • 开发人员希望转换字符串
      • 转换是要颠倒所有字符的顺序

    解决方案

    • 示例01使用扩展切片表示法生成所需的结果。

    陷阱

    • 开发人员可能会期望类似于string.reverse()的东西
    • 本机惯用(又称为"Python")解决方案可能对新开发人员不可读。
    • 开发人员可能会试图实现他或她自己的EDOCX1版本(0),以避免切片表示法。
    • 在某些情况下,切片符号的输出可能是反直觉的:
      • 参见示例02
        • print 'coup_ate_grouping'[-4:] ## => 'ping'
        • 相比于
        • print 'coup_ate_grouping'[-4:-1] ## => 'pin'
        • 相比于
        • print 'coup_ate_grouping'[-1] ## => 'g'
      • [-1]上建立索引的不同结果可能会让一些开发者望而却步。

    理论基础

    python有一个需要注意的特殊情况:字符串是一个可Iterable类型。

    排除string.reverse()方法的一个基本原理是激励Python开发人员利用这种特殊情况的力量。

    简单地说,这意味着字符串中的每个单独字符都可以作为元素顺序排列的一部分进行操作,就像其他编程语言中的数组一样。

    为了了解这是如何工作的,回顾示例02可以提供一个很好的概述。

    例02

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    ### 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中的工作方式所带来的认知负担可能确实太多了。

    然而,一旦理解了基本原理,这种方法对固定字符串操作方法的威力就会非常有利。

    对于那些不这么认为的人,有其他方法,例如lambda函数、迭代器或简单的一次性函数声明。

    如果需要,开发人员可以实现自己的string.reverse()方法,但是理解Python这一方面的基本原理是很好的。

    也见

    • 替代简单方法
    • 替代简单方法
    • 切片符号的交替解释


    一个不那么令人费解的方法是:

    1
    2
    string = 'happy'
    print(string)

    'happy'

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

    'yppah'

    英语中[-1::-1]读作:

    "Starting at -1, go all the way, taking steps of -1"


    1。使用切片表示法

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

    2。使用Reversed()函数

    1
    2
    def rev_string(s):
        return ''.join(reversed(s))

    三。使用递归

    1
    2
    3
    4
    5
    def rev_string(s):
        if len(s) == 1:
            return s

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


    在python中反转字符串,而不使用reversed()或[::-1]

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


    这也是一种有趣的方式:

    1
    2
    3
    4
    5
    6
    def reverse_words_1(s):
        rev = ''
        for i in range(len(s)):
            j = ~i  # equivalent to j = -(i + 1)
            rev += s[j]
        return rev

    或类似的:

    1
    2
    3
    4
    5
    def reverse_words_2(s):
        rev = ''
        for i in reversed(range(len(s)):
            rev += s[i]
        return rev

    另一种更"异域"的方法是使用支持.reverse()的byterarray

    1
    2
    3
    b = bytearray('Reverse this!', 'UTF-8')
    b.reverse()
    b.decode('UTF-8')

    将产生:

    1
    '!siht esreveR'

    1
    2
    3
    4
    5
    6
    7
    8
    original ="string"

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

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


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


    这里有一个不花哨的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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中反向使用for循环的字符串,将会变得有点棘手,因此下面介绍如何使用for循环反向字符串

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

    我希望这个能对某人有所帮助。


    这就是我的方式:

    1
    2
    3
    4
    5
    6
    7
    8
    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
    def reverse(s): return s[0] if len(s)==1 else s[len(s)-1] + reverse(s[0:len(s)-1])

    例子:

    1
    print(reverse("Hello!"))    #!olleH


    反转一个没有python魔法的字符串。

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

    1
    2
    3
    4
    5
    6
    7
    8
    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"

    这通过循环遍历一个字符串并将其值按相反顺序分配给另一个字符串来实现。


    这里有一个没有[::-1]reversed的(用于学习目的):

    1
    2
    3
    4
    5
    6
    7
    8
    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()更快。


    这里简单地说:

    打印"loremipsum"[-1::-1]

    还有一些逻辑上的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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()

    输出:

    麝香草酚


    这是一个简单而有意义的逆向函数,易于理解和编码

    1
    2
    3
    4
    5
    6
    def reverse_sentence(text):
        words = text.split("")
        reverse =""
        for word in reversed(words):
            reverse += word+""
        return reverse


    S='你好,世界'

    S:(::- 1)

    在上面的示例中,标签s或变量s持有包含hello world字符串的字符串,在第二步中,我将以-1的相反步骤从所有内容开始打印hello world字符串的反转。


    当然,在python中,你可以做一些非常漂亮的1行代码。:)这里有一个简单、全面的解决方案,可以在任何编程语言中工作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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
    2
    3
    4
    5
    6
    7
    8
    9
    s = 'hello'
    ln = len(s)
    i = 1
    while True:
        rev = s[ln-i]
        print rev,
        i = i + 1
        if i == ln + 1 :
            break

    输出:

    1
    o l l e h


    您可以将reversed函数与list compressive一起使用。但我不明白为什么在Python3中消除了这个方法,这是不必要的。

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