协慌网

登录 贡献 社区

在 Python 中反转一个字符串

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

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

答案

怎么样:

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

这是扩展切片语法。它的工作原理是[begin:end:step] - 通过离开开始和结束并指定步长为 - 1,它会反转一个字符串。

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

为字符串实现反向函数的最佳方法是什么?

我对这个问题的经验是学术性的。但是,如果您是专业人士寻找快速答案,请使用步长为-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'

更长的解释

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

Python 的 str 对象中没有内置的反向函数。

这里有一些你应该知道的 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()形式。这将确保在各种实现中以线性时间进行连接。