题 在Python中反转一个字符串


没有内置的 reverse Python的功能 str 目的。实现此方法的最佳方法是什么?

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


1071
2018-05-31 02:10


起源




答案:


怎么样:

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

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


2220
2018-05-31 02:11



虽然这对utf8不起作用..我也需要这样做 b = a.decode('utf8')[::-1].encode('utf8') 但感谢正确的方向! - Ricky Levi
@RickyLevi如果 .decode('utf8') 是必需的,这意味着 a 不包含任何字符串对象,而是包含字节。 - Shiplu Mokaddim
@RickyLevi我能够在没有解码和编码的情况下反转utf8字符串。 - Himadri
@Himadri无论是Python 3还是Python 2.7都很重要 - Csaba Toth
我用过这个和它的组合 join 回答以下问题,生成脚本的(假)发送电子邮件地址。 scriptname="".join(os.path.basename(sys.argv[0])[::-1].split('.')[1:])[::-1] ; computername=socket.gethostname() ; msgfrom=scriptname + '_on_' + computername + '@example.com' - user208145


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


217
2018-05-31 02:13



这大约慢3倍。 - oneself
并且快速评论说它的作用将比使用这个更慢的版本更好地解释它! - tburrows13
它比较慢,因为 join  具有 无论如何要建立列表以便能够获得大小。 ''.join(list(reversed(s))) 可能会稍快一点。 - Jean-François Fabre


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

我对这个问题的经验是学术性的。但是,如果您是专业人士正在寻找快速答案,请使用逐步处理的切片 -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()形式。这将确保在各种实现中以线性时间进行连接。


168
2018-01-08 15:32





快速回答(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的以下问题:

哇。起初Paolo提出的解决方案让我感到震惊,但那   我在阅读第一篇文章后感到恐惧,退居二线   评论:“这是非常pythonic。干得好!”我很不安   一个聪明的社区认为使用这种神秘的方法   基本是个好主意。为什么不是s.reverse()?

问题

  • 上下文
    • Python 2.x
    • Python 3.x
  • 场景:
    • 开发人员想要转换字符串
    • 转换是颠倒所有角色的顺序

陷阱

  • 开发人员可能期待类似的东 string.reverse()
  • 本土的惯用语(又名“Python的“)解决方案对于较新的开发人员可能无法读取
  • 开发人员可能会试图实现他或她自己的版本 string.reverse() 避免切片表示法。
  • 在某些情况下,切片表示法的输出可能是违反直觉的:
    • 参见例如example02
      • print 'coup_ate_grouping'[-4:] ## => 'ping'
      • 相比
      • print 'coup_ate_grouping'[-4:-1] ## => 'pin'
      • 相比
      • print 'coup_ate_grouping'[-1] ## => 'g'
    • 索引的不同结果 [-1] 可能会让一些开发者失望

合理

Python有一个特殊的环境需要注意:字符串是一个 迭代 类型。

排除a的一个理由 string.reverse() 方法是让python开发人员激励利用这种特殊情况的力量。

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

要了解其工作原理,查看example02可以提供良好的概述。

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'

结论

认知负荷 与理解切片符号在python中如何工作相关联对于那些不希望花费大量时间学习语言的采用者和开发者来说确实太过分了。

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

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

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

也可以看看


33
2017-10-31 22:24





看待它的一个较小的令人困惑的方式是:

string = 'happy'
print(string)

'快乐'

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

“帕”

英文[-1 :: - 1]读作:

“从-1开始,一路走,步长为-1”


10
2018-04-01 07:49





在不使用reverse()或[:: - 1]的情况下在python中反转字符串

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

4
2017-12-10 12:57



你不应该使用xrange,因为你不需要列表,在python 2中? - UnitasBrooks


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

3
2018-06-26 04:25



我点击了upvote,因为我喜欢这个lambda表达式。不幸的是,它是上面列出的所有效率最低的解决方案(测试: 要点palindrome.py ) - oski86


使用切片表示法

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

使用reversed()函数

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

使用递归

def rev_string(s): 
    if len(s) == 1:
        return s

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

3
2018-05-20 22:24





这是一个不花哨的:

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!")

2
2018-05-04 17:02





另一种选择,(效率不高!只是为了展示Python的多样性以及许多可能的解决方案!):使用list()函数将字符串转换为列表。列表值是可变数据类型。因此,我们可以使用reverse()方法来反转列表中的对象。然后我们使用带有空分隔符的列表连接方法将列表转换回字符串:

>>> s = 'hello world'
>>> s
'hello world'
>>> t = list(s) # convert to list
>>> t
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
>>> t.reverse() # reverse method of list
>>> t
['d', 'l', 'r', 'o', 'w', ' ', 'o', 'l', 'l', 'e', 'h']
>>> s = ''.join(t) # convert to string
>>> s
'dlrow olleh'

2
2017-12-26 11:12





这是一个没有 [::-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() 是比较快的。


1
2017-12-29 13:23