反转Python中的字符串

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

若是提供很是简洁的答案,请详细说明其效率。 例如,是否将str对象转换为其余对象等。 python


#1楼

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

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

#2楼

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

我对这个问题的经验是学术上的。 可是,若是您是专业人士,正在寻找快速解答,请使用以-1步长的切片: 编程

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

或可读性更好(但因为方法名称查找和加入给定迭代器时join造成列表的事实而变慢), str.join数组

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

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

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

接着: 编程语言

>>> reversed_string('a_string')
'gnirts_a'

更长的解释

若是您对学术博览会感兴趣,请继续阅读。 ide

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

您应该了解如下有关Python字符串的几件事: 性能

  1. 在Python中, 字符串是不可变的 。 更改字符串不会修改该字符串。 它建立了一个新的。

  2. 字符串是可切片的。 分割字符串会以给定的增量从字符串的一个点向后或向前,再到另外一点,为您提供一个新的字符串。 它们在下标中采用切片符号或切片对象:

    string[subscript]

下标经过在括号内包含冒号来建立切片:

string[start:stop:step]

要在大括号以外建立切片,您须要建立一个slice对象:

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]

可是,对于不熟悉切片或原始做者意图的人,咱们如何使它更具可读性和可理解性? 让咱们在下标符号以外建立一个slice对象,为其指定一个描述性名称,而后将其传递给下标符号。

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()形式。 这将确保在各类实现中串联发生在线性时间内。


#3楼

这是一个没有幻想的:

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

#4楼

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

#5楼

快速解答(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提出的解决方案感到震惊,但这让我在读了第一条评论时感到恐惧,这让我感到恐惧:“那太好了。作得好!” 我感到很是不安,以致于如此聪明的社区认为将如此神秘的方法用于如此基本的东西是一个好主意。 为何不仅是s.reverse()?

问题

  • 语境
    • Python 2.x
    • Python 3.x
  • 场景:
    • 开发人员想要转换字符串
    • 转换是颠倒全部字符的顺序

陷阱

  • 开发人员可能指望像string.reverse()这样的东西
  • 较新的开发人员可能没法阅读本机惯用(也称为“ pythonic ”)解决方案
  • 开发人员可能会尝试实现本身的string.reverse()版本以免切片符号。
  • 在某些状况下,切片符号的输出多是违反直觉的:
    • 参见例如example02
      • 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能够提供很好的概述。

范例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中工做的人来讲, 认知负担确实对于那些不肯意在学习该语言上花费大量时间的采用者和开发人员来讲实在太大了。

可是,一旦理解了基本原理,此方法相对于固定字符串操做方法的功能可能会很是有利。

对于那些有其余想法的人,还有其余方法,例如lambda函数,迭代器或简单的一次性函数声明。

若是须要,开发人员能够实现本身的string.reverse()方法,可是最好理解python这方面的原理。

也能够看看

相关文章
相关标签/搜索