Pythonで文字列を逆にする


1351

reversePythonのstrオブジェクトの組み込み関数はありません。この方法を実装する最良の方法は何ですか?

非常に簡潔な回答を提供する場合は、その効率について詳しく説明してください。たとえば、strオブジェクトが別のオブジェクトに変換されるかどうかなど。


1
文字列「tacocat」を使用
JonPizza

回答:


2653

どうですか:

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

これは拡張スライス構文です。これは次のように機能し[begin:end:step]ます-beginとendを残して、-1のステップを指定することにより、文字列を逆にします。


29
これはutf8では機能しませんが、私もこれを行う必要b = a.decode('utf8')[::-1].encode('utf8')がありましたが、正しい方向に感謝します!
リッキーレヴィ

14
@RickyLevi .decode('utf8')必要な場合はa、バイトではなく文字列オブジェクトを含まないことを意味します。
Shiplu Mokaddim 2017年

256

@Paolo s[::-1]は最速です。より遅いアプローチ(より読みやすいかもしれませんが、議論の余地があります)は''.join(reversed(s))です。


14
これは約3倍遅くなります。
自分

2
そして、それが何をするのかを言う簡単なコメントは、この遅いバージョンを使用するよりもそれをよりよく説明します!
tburrows13

4
ので、それは遅くなりますjoin 持ってとにかくリストを構築するためにサイズを取得できるようにします。''.join(list(reversed(s)))少し速いかもしれません。
ジャン=フランソワ・ファーブル

[::-1]が最も速い理由について何か情報はありますか?もっと詳しく知りたいのですが。
タナー

@Tanner [::-1]は、外部関数を呼び出さず、Pythonで高度に最適化されたスライシングを使用しているため、最も高速です。'' .join(list(reversed(s)))は3つの関数呼び出しを行います。
hd1

217

文字列の逆関数を実装する最良の方法は何ですか?

この質問に対する私自身の経験は学問的です。ただし、簡単な答えを探しているプロの場合は、次のようなスライスを使用します-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は文字列連結を最適化しますが、他の実装はそうではないかもしれません

... a + = bまたはa = a + bの形式のステートメントに対して、CPythonのインプレース文字列連結の効率的な実装に依存しないでください。この最適化はCPythonでも脆弱であり(一部のタイプでのみ機能します)、refcountingを使用しない実装にはまったく存在しません。ライブラリのパフォーマンスに敏感な部分では、代わりに '' .join()フォームを使用する必要があります。これにより、さまざまな実装間で連結が線形時間で行われることが保証されます。


5
私はこの答え、最適化に関する説明、読みやすさと最適化、教師が望んでいることに関するヒントが大好きです。whileおそらくこれは読みにくいですが、とインデックスのデクリメントのベストプラクティスセクションについてはわかりません for i in range(len(a_string)-1, -1, -1): 。あなたが選択した例文字列は、あなたがそれを逆にする必要はありませんでしょう一つのケースで、あなたは:)を持っていた場合伝えることができないだろうことを、すべてのI愛のほとんどは
ダボス

37

クイックアンサー(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からの次の懸念に対処するために提供されています。

ワオ。パオロが提案した解決策に最初は恐れていましたが、それは最初のコメントを読んだときに感じた恐怖に後ずさりしました。私はとても混乱しているので、そのような明るいコミュニティは、非常に基本的な何かのためにそのような不可解な方法を使用することをお勧めします。なぜ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()メソッドを除外する根拠の1つは、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でどのように機能するかを理解することに関連付けられているが、実際の言語を学ぶことに多くの時間を投資したくないいくつかの採用や開発者のためにあまりにも多くのことがあります。

それにもかかわらず、基本的な原則が理解されれば、固定文字列操作メソッドに対するこのアプローチの力は非常に有利になる可能性があります。

そうでないと考える人のために、ラムダ関数、イテレータ、単純な1回限りの関数宣言などの代替アプローチがあります。

必要に応じて、開発者は独自のstring.reverse()メソッドを実装できますが、Pythonのこの側面の背後にある理論的根拠を理解することは良いことです。

こちらもご覧ください


17

既存の回答は、Unicode Modifiers /書記素クラスターが無視された場合にのみ正しいです。これについては後で扱いますが、最初にいくつかの反転アルゴリズムの速度を確認します。

ここに画像の説明を入力してください

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

ここに画像の説明を入力してください

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

リストの内包化(reversed = string[::-1])の時間がすべてのケースで(私のタイプミスを修正した後でも)最も低いことがわかります。

文字列反転

常識的に文字列を反転したい場合は、もっと複雑です。たとえば、次の文字列(茶色の指が左を指し黄色の指が上を指している)を取ります。これらは2つの書記素ですが、3つのUnicodeコードポイントです。追加の1つはスキンモディファイヤです。

example = "👈🏾👆"

しかし、与えられた方法のいずれかでそれを逆にすると、茶色の指が上向きになり黄色の指が左向きになります。この理由は、「茶色」のカラーモディファイアがまだ中央にあり、その前にあるものすべてに適用されるためです。だから私たちは

  • U:上向きの指
  • M:茶色の修飾子
  • L:左向きの指

そして

original: LMU
reversed: UML (above solutions)
reversed: ULM (correct reversal)

Unicode書記素クラスターは、修飾子のコードポイントよりも少し複雑です。幸い、書記素を処理するためのライブラリがあります。

>>> import grapheme
>>> g = grapheme.graphemes("👈🏾👆")
>>> list(g)
['👈🏾', '👆']

したがって、正しい答えは

def reverse_graphemes(string):
    g = list(grapheme.graphemes(string))
    return ''.join(g[::-1])

これもはるかに遅いです:

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

コード

#!/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()

10

1.スライス表記の使用

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

2. reverse()関数の使用

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

3.再帰の使用

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

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

1
文字列が適切な長さの場合は、再帰ソリューションに注意してくださいRecursionError: maximum recursion depth exceeded while calling a Python object。例:rev_string("abcdef"*1000)
アダムパーキン

9

それを見るためのそれほど複雑ではない方法は次のようになります:

string = 'happy'
print(string)

'ハッピー'

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

「イッパ」

英語では[-1 ::-1]は次のように読みます。

「-1から始めて、-1のステップを踏んでいきます。」


2
-1けれども、まだ不要です。
Eric Duminil

7

reverse()や[::-1]を使わずにPythonで文字列を逆にします

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

1
Python 2では、リストが必要ないのでxrangeを使用するべきではありませんか?
UnitasBrooks

5

これも興味深い方法です。

def reverse_words_1(s):
    rev = ''
    for i in range(len(s)):
        j = ~i  # equivalent to j = -(i + 1)
        rev += s[j]
    return rev

または類似:

def reverse_words_2(s):
    rev = ''
    for i in reversed(range(len(s)):
        rev += s[i]
    return rev

.reverse()をサポートするbyterarrayを使用したもう1つの「エキゾチック」な方法

b = bytearray('Reverse this!', 'UTF-8')
b.reverse()
b.decode('UTF-8')

生成されます:

'!siht esreveR'

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

3
私はこのラムダ式が好きなので、upvoteをクリックしました。残念ながら、これは上記のすべての中で最も効率の悪いソリューションです(テスト:Gist palindrome.py
oski86

2
original = "string"

rev_index = original[::-1]
rev_func = list(reversed(list(original))) #nsfw

print(original)
print(rev_index)
print(''.join(rev_func))

1
このコードは質問に答えることがありますが、問題を解決する方法を説明し、例または参照としてコードを提供することをお勧めします。コードのみの回答は混乱しやすく、コンテキストが不足している可能性があります。
ロバートコロンビア

1
def reverse_string(string):
    length = len(string)
    temp = ''
    for i in range(length):
        temp += string[length - i - 1]
    return temp

print(reverse_string('foo')) #prints "oof"

これは、文字列をループして、その値を逆の順序で別の文字列に割り当てることによって機能します。


0

ここに派手なものはありません:

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

0

ここに、[::-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()、高速です。


0

再帰的な方法:

def reverse(s): return s[0] if len(s)==1 else s[len(s)-1] + reverse(s[0:len(s)-1])

例:

print(reverse("Hello!"))    #!olleH

0

上記のソリューションはすべて完璧ですが、Pythonでforループを使用して文字列を逆転させようとすると、少しトリッキーになるため、forループを使用して文字列を逆転させる方法を次に示します。

string ="hello,world"
for i in range(-1,-len(string)-1,-1):
    print (string[i],end=(" ")) 

これが誰かのために役立つことを願っています。


0

それが私のやり方だ:

def reverse_string(string):
    character_list = []
    for char in string:
        character_list.append(char)
    reversed_string = ""
    for char in reversed(character_list):
        reversed_string += char
    return reversed_string

0

文字列を逆にする方法はたくさんありますが、楽しみのために別の方法も作成しました。このアプローチはそれほど悪くはないと思います。

def reverse(_str):
    list_char = list(_str) # Create a hypothetical list. because string is immutable

    for i in range(len(list_char)/2): # just t(n/2) to reverse a big string
        list_char[i], list_char[-i - 1] = list_char[-i - 1], list_char[i]

    return ''.join(list_char)

print(reverse("Ehsan"))

0

このクラスは、Pythonマジック関数を使用して文字列を反転します。

class Reverse(object):
    """ Builds a reverse method using magic methods """

    def __init__(self, data):
        self.data = data
        self.index = len(data)


    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration

        self.index = self.index - 1
        return self.data[self.index]


REV_INSTANCE = Reverse('hello world')

iter(REV_INSTANCE)

rev_str = ''
for char in REV_INSTANCE:
    rev_str += char

print(rev_str)  

出力

dlrow olleh

参照


-1

Python 3では、文字列をインプレースで逆にすることができます。つまり、別の変数に割り当てられません。まず、文字列をリストに変換してから、reverse()関数を利用する必要があります。

https://docs.python.org/3/tutorial/datastructures.html

   def main():
        my_string = ["h","e","l","l","o"]
        print(reverseString(my_string))

    def reverseString(s):
      print(s)
      s.reverse()
      return s

    if __name__ == "__main__":
        main()

-2

これはシンプルで意味のある逆関数であり、理解とコーディングが簡単です

def reverse_sentence(text):
    words = text.split(" ")
    reverse =""
    for word in reversed(words):
        reverse += word+ " "
    return reverse

これは著者の質問に答えるかもしれませんが、説明する単語やドキュメントへのリンクが不足しています。生のコードスニペットは、周りにいくつかのフレーズがないとあまり役に立ちません。また、良い答えの書き方も非常に役立ちます。回答を編集してください。
2018

-3

ここに簡単です:

「loremipsum」[-1 ::-1]を印刷します

そしていくつかは論理的に:

def str_reverse_fun():
    empty_list = []
    new_str = 'loremipsum'
    index = len(new_str)
    while index:
        index = index - 1
        empty_list.append(new_str[index])
    return ''.join(empty_list)
print str_reverse_fun()

出力:

ムスピメロール


-4

Pythonマジックなしで文字列を反転します。

>>> def reversest(st):
    a=len(st)-1
    for i in st:
        print(st[a],end="")
        a=a-1

文字列を逆に印刷することは、文字列を逆にすることとは異なります
Martin Thoma

-5

もちろん、Pythonでは非常に豪華な1行のコードを実行できます。:)
これは、あらゆるプログラミング言語で機能する、シンプルで丸みを帯びたソリューションです。

def reverse_string(phrase):
    reversed = ""
    length = len(phrase)
    for i in range(length):
        reversed += phrase[length-1-i]
    return reversed

phrase = raw_input("Provide a string: ")
print reverse_string(phrase)

1
そのような些細なタスクのためにそのような長いコードを持つことは良い解決策ではありません。
Hunter_71 2017年

-5
s = 'hello'
ln = len(s)
i = 1
while True:
    rev = s[ln-i]
    print rev,
    i = i + 1
    if i == ln + 1 :
        break

出力:

o l l e h

-7

リスト包括的で反転機能を使用できます。しかし、なぜこの方法がpython 3で削除されたのか理解できません。

string = [ char for char in reversed(string)]

質問は文字列の逆を求め、代わりにリストを与えますか?
user21820 2017

あなたは.joinそれを有効な答えにするために何かが必要です
AsheKetchum

1
ところで、[c for c in string]と同じlist(string)です。
右レッグ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.