Pao*_*ino 2565
怎么样:
>>> 'hello world'[::-1]
'dlrow olleh'
Run Code Online (Sandbox Code Playgroud)
这是扩展切片语法.它的工作原理是[begin:end:step]
- 通过离开开始和结束并指定步长为-1,它会反转一个字符串.
Ale*_*lli 252
@ Paolo s[::-1]
是最快的; 一种较慢的方法(可能更具可读性,但这是有争议的)''.join(reversed(s))
.
Aar*_*all 208
为字符串实现反向函数的最佳方法是什么?
我对这个问题的经验是学术性的.但是,如果您是专业人士寻找快速答案,请使用逐步执行的切片-1
:
>>> 'a string'[::-1]
'gnirts a'
Run Code Online (Sandbox Code Playgroud)
或者更可读(但由于方法名称查找速度较慢而且在给定迭代器时连接形成列表的事实)str.join
:
>>> ''.join(reversed('a string'))
'gnirts a'
Run Code Online (Sandbox Code Playgroud)
或者为了可读性和可重用性,将切片放在一个函数中
def reversed_string(a_string):
return a_string[::-1]
Run Code Online (Sandbox Code Playgroud)
然后:
>>> reversed_string('a_string')
'gnirts_a'
Run Code Online (Sandbox Code Playgroud)
如果您对学术博览会感兴趣,请继续阅读.
Python的str对象中没有内置的反向函数.
这里有一些你应该知道的Python字符串:
在Python中,字符串是不可变的.更改字符串不会修改字符串.它创造了一个新的.
字符串是可切片的.切片字符串会以给定的增量为您提供从字符串中的一个点向后或向前到另一个点的新字符串.它们在下标中采用切片表示法或切片对象:
string[subscript]
Run Code Online (Sandbox Code Playgroud)下标通过在大括号中包含冒号来创建切片:
string[start:stop:step]
Run Code Online (Sandbox Code Playgroud)
要在大括号外创建切片,您需要创建切片对象:
slice_obj = slice(start, stop, step)
string[slice_obj]
Run Code Online (Sandbox Code Playgroud)
虽然''.join(reversed('foo'))
是可读的,但它需要str.join
在另一个被调用的函数上调用字符串方法,这可能相当慢.让我们把它放在一个函数中 - 我们将回到它:
def reverse_string_readable_answer(string):
return ''.join(reversed(string))
Run Code Online (Sandbox Code Playgroud)
使用反向切片要快得多:
'foo'[::-1]
Run Code Online (Sandbox Code Playgroud)
但是,对于不太熟悉切片或原作者意图的人,我们怎样才能使这个更具可读性和易懂性?让我们在下标表示法之外创建一个切片对象,给它一个描述性名称,并将其传递给下标符号.
start = stop = None
step = -1
reverse_slice = slice(start, stop, step)
'foo'[reverse_slice]
Run Code Online (Sandbox Code Playgroud)
要将其实际实现为一个函数,我认为它在语义上足够清楚,只需使用描述性名称:
def reversed_string(a_string):
return a_string[::-1]
Run Code Online (Sandbox Code Playgroud)
用法很简单:
reversed_string('foo')
Run Code Online (Sandbox Code Playgroud)
如果你有一个教师,他们可能希望你从一个空字符串开始,并从旧的字符串建立一个新的字符串.您可以使用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
Run Code Online (Sandbox Code Playgroud)
这在理论上是不好的,因为,记住,字符串是不可变的 - 所以每次看起来你在你的字符上附加一个字符时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)
Run Code Online (Sandbox Code Playgroud)
但是,正如我们将在下面的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
Run Code Online (Sandbox Code Playgroud)
CPython优化字符串连接,而其他实现可能不:
...不要依赖CPython为a + = b或a = a + b形式的语句高效实现就地字符串连接.即使在CPython中,这种优化也很脆弱(它只适用于某些类型),并且在不使用引用计数的实现中根本不存在.在库的性能敏感部分,应该使用'.join()形式.这将确保在各种实现中以线性时间进行连接.
dre*_*mac 37
### example01 -------------------
mystring = 'coup_ate_grouping'
backwards = mystring[::-1]
print backwards
### ... or even ...
mystring = 'coup_ate_grouping'[::-1]
print mystring
### result01 -------------------
'''
gnipuorg_eta_puoc
'''
Run Code Online (Sandbox Code Playgroud)
提供此答案是为了解决@odigity的以下问题:
哇.起初Paolo提议的解决方案让我感到震惊,但是在阅读第一条评论时我感到恐惧的后座:"这是非常pythonic.干得好!" 我很不安,这样一个聪明的社区认为使用这种神秘的方法来做一些基本的东西是个好主意.为什么不是s.reverse()?
string.reverse()
string.reverse()
以避免切片表示法.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 -------------------
## 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'
Run Code Online (Sandbox Code Playgroud)
与理解切片符号在python中如何工作相关的认知负荷对于那些不希望花费大量时间学习语言的采用者和开发者来说确实太过分了.
然而,一旦理解了基本原理,这种方法相对于固定字符串操作方法的能力就会非常有利.
对于那些不这么认为的人,有其他方法,例如lambda函数,迭代器或简单的一次性函数声明.
如果需要,开发人员可以实现自己的string.reverse()方法,但是理解python这方面的基本原理是很好的.
Mar*_*oma 12
仅当忽略Unicode修饰符/字形群集时,现有答案才是正确的。我将在稍后处理,但首先请看一些反转算法的速度:
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
您可以看到,列表推导(reversed = string[::-1]
)的时间在所有情况下都是最低的(即使在修正我的错字之后)。
如果您真的想按常识反转字符串,则方法会更加复杂。例如,采用以下字符串(棕色手指指向左,黄色手指指向上)。那是两个字素,但有3个unicode码点。另一个是皮肤修饰剂。
example = ""
Run Code Online (Sandbox Code Playgroud)
但是,如果使用任何给定的方法将其反转,则会使棕色手指指向上方,黄色手指指向左侧。这样做的原因是“棕色”颜色修改器仍在中间,并应用于之前的任何内容。所以我们有
和
original: LMU
reversed: UML (above solutions)
reversed: ULM (correct reversal)
Run Code Online (Sandbox Code Playgroud)
Unicode音素簇比修饰符代码点要复杂一些。幸运的是,用于处理库字形:
>>> import grapheme
>>> g = grapheme.graphemes("")
>>> list(g)
['', '']
Run Code Online (Sandbox Code Playgroud)
因此正确的答案是
def reverse_graphemes(string):
g = list(grapheme.graphemes(string))
return ''.join(g[::-1])
Run Code Online (Sandbox Code Playgroud)
到目前为止也是最慢的:
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
Run Code Online (Sandbox Code Playgroud)
#!/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()
Run Code Online (Sandbox Code Playgroud)
pX0*_*X0r 10
看待它的一个较小的令人困惑的方式是:
string = 'happy'
print(string)
Run Code Online (Sandbox Code Playgroud)
'快乐'
string_reversed = string[-1::-1]
print(string_reversed)
Run Code Online (Sandbox Code Playgroud)
"帕"
英文[-1 :: - 1]读作:
"从-1开始,一路走,步长为-1"
har*_*rry 10
def rev_string(s):
return s[::-1]
Run Code Online (Sandbox Code Playgroud)
def rev_string(s):
return ''.join(reversed(s))
Run Code Online (Sandbox Code Playgroud)
def rev_string(s):
if len(s) == 1:
return s
return s[-1] + rev_string(s[:-1])
Run Code Online (Sandbox Code Playgroud)
在不使用reverse()或[:: - 1]的情况下在python中反转字符串
def reverse(test):
n = len(test)
x=""
for i in range(n-1,-1,-1):
x += test[i]
return x
Run Code Online (Sandbox Code Playgroud)
这也是一种有趣的方式:
def reverse_words_1(s):
rev = ''
for i in range(len(s)):
j = ~i # equivalent to j = -(i + 1)
rev += s[j]
return rev
Run Code Online (Sandbox Code Playgroud)
或类似:
def reverse_words_2(s):
rev = ''
for i in reversed(range(len(s)):
rev += s[i]
return rev
Run Code Online (Sandbox Code Playgroud)
使用支持.reverse()的BYTERArray的另一种“异国情调”方式
b = bytearray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')
Run Code Online (Sandbox Code Playgroud)
将产生:
'!siht esreveR'
Run Code Online (Sandbox Code Playgroud)