反轉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這方面的原理。

也能夠看看

相關文章
相關標籤/搜索