web-dev-qa-db-ja.com

Pythonでの文字列連結と文字列置換

Pythonでは、文字列の連結と文字列の置換を使用する場所と時期がわかりません。文字列の連結によりパフォーマンスが大幅に向上しているため、これは(より多くなる)実用的な決定ではなく、スタイル上の決定ですか?

具体的な例として、柔軟なURIの構築をどのように処理する必要がありますか。

DOMAIN = 'http://stackoverflow.com'
QUESTIONS = '/questions'

def so_question_uri_sub(q_num):
    return "%s%s/%d" % (DOMAIN, QUESTIONS, q_num)

def so_question_uri_cat(q_num):
    return DOMAIN + QUESTIONS + '/' + str(q_num)

編集:文字列のリストへの参加および名前付き置換の使用に関する提案もありました。これらは中心テーマの変種です。つまり、どの時点でそれを行うのが正しい方法ですか。回答ありがとうございます!

98
gotgenes

私のマシンによれば、連結は(かなり)高速です。しかし、スタイル的には、パフォーマンスが重要でない場合は、代用の代価を払っても構わないと思っています。さて、フォーマットが必要な場合は、質問する必要さえありません...補間/テンプレートを使用する以外に選択肢はありません。

>>> import timeit
>>> def so_q_sub(n):
...  return "%s%s/%d" % (DOMAIN, QUESTIONS, n)
...
>>> so_q_sub(1000)
'http://stackoverflow.com/questions/1000'
>>> def so_q_cat(n):
...  return DOMAIN + QUESTIONS + '/' + str(n)
...
>>> so_q_cat(1000)
'http://stackoverflow.com/questions/1000'
>>> t1 = timeit.Timer('so_q_sub(1000)','from __main__ import so_q_sub')
>>> t2 = timeit.Timer('so_q_cat(1000)','from __main__ import so_q_cat')
>>> t1.timeit(number=10000000)
12.166618871951641
>>> t2.timeit(number=10000000)
5.7813972166853773
>>> t1.timeit(number=1)
1.103492206766532e-05
>>> t2.timeit(number=1)
8.5206360154188587e-06

>>> def so_q_tmp(n):
...  return "{d}{q}/{n}".format(d=DOMAIN,q=QUESTIONS,n=n)
...
>>> so_q_tmp(1000)
'http://stackoverflow.com/questions/1000'
>>> t3= timeit.Timer('so_q_tmp(1000)','from __main__ import so_q_tmp')
>>> t3.timeit(number=10000000)
14.564135316080637

>>> def so_q_join(n):
...  return ''.join([DOMAIN,QUESTIONS,'/',str(n)])
...
>>> so_q_join(1000)
'http://stackoverflow.com/questions/1000'
>>> t4= timeit.Timer('so_q_join(1000)','from __main__ import so_q_join')
>>> t4.timeit(number=10000000)
9.4431309007150048
55
Vinko Vrsalovic

名前付き置換を忘れないでください:

def so_question_uri_namedsub(q_num):
    return "%(domain)s%(questions)s/%(q_num)d" % locals()
23
too much php

ループ内の文字列の連結には注意してください!文字列連結のコストは、結果の長さに比例します。ループを使用すると、N平方の土地に直接移動できます。一部の言語では、最後に割り当てられた文字列への連結が最適化されますが、コンパイラを使用して2次アルゴリズムを線形に最適化するのは危険です。文字列のリスト全体を受け取り、単一の割り当てを行い、それらをすべて一度に連結するプリミティブ(join?)を使用するのが最適です。

12
Norman Ramsey

「文字列の連結によりパフォーマンスが大幅に向上したため...」

パフォーマンスが重要な場合、これは知っておくと便利です。

しかし、私が見たパフォーマンスの問題は、文字列操作にまで至っていません。私は通常、I/O、ソート、O(n2)ボトルネックとなっている操作。

文字列操作がパフォーマンスリミッターになるまで、私は明白なものに固執します。ほとんどの場合、1行以下の場合は置換、意味のある場合は連結、サイズが大きい場合はテンプレートツール(Makoなど)です。

11
S.Lott

連結/補間するものと結果のフォーマット方法によって、決定が下されます。

  • 文字列補間により、書式設定を簡単に追加できます。実際、文字列補間バージョンは、連結バージョンと同じことを行いません。 _q_num_パラメータの前に実際に余分なスラッシュを追加します。同じことを行うには、その例でreturn DOMAIN + QUESTIONS + "/" + str(q_num)を書く必要があります。

  • 補間により、数値の書式設定が簡単になります。 "%d of %d (%2.2f%%)" % (current, total, total/current)は連結形式でははるかに読みにくくなります。

  • 連結は、文字列化するアイテムの数が決まっていない場合に便利です。

また、Python 2.6は string templating と呼ばれる新しいバージョンの文字列補間を導入することを知っています。

_def so_question_uri_template(q_num):
    return "{domain}/{questions}/{num}".format(domain=DOMAIN,
                                               questions=QUESTIONS,
                                               num=q_num)
_

文字列テンプレートは、最終的に%補間に置き換わる予定ですが、それはしばらくの間は起こらないと思います。

10
Tim Lesher

好奇心からさまざまな文字列の連結/置換方法の速度をテストしていました。主題のグーグル検索は私をここに連れて来ました。誰かの判断に役立つと期待して、テスト結果を投稿すると思いました。

    import timeit
    def percent_():
            return "test %s, with number %s" % (1,2)

    def format_():
            return "test {}, with number {}".format(1,2)

    def format2_():
            return "test {1}, with number {0}".format(2,1)

    def concat_():
            return "test " + str(1) + ", with number " + str(2)

    def dotimers(func_list):
            # runs a single test for all functions in the list
            for func in func_list:
                    tmr = timeit.Timer(func)
                    res = tmr.timeit()
                    print "test " + func.func_name + ": " + str(res)

    def runtests(func_list, runs=5):
            # runs multiple tests for all functions in the list
            for i in range(runs):
                    print "----------- TEST #" + str(i + 1)
                    dotimers(func_list)

...runtests((percent_, format_, format2_, concat_), runs=5)を実行した後、これらの小さな文字列で%メソッドが他のメソッドの約2倍高速であることがわかりました。 concatメソッドは常に(ほとんど)最も低速でした。 format()メソッドで位置を切り替える際には非常に小さな違いがありましたが、位置の切り替えは常に通常のフォーマット方式よりも少なくとも.01遅くなりました。

テスト結果のサンプル:

    test concat_()  : 0.62  (0.61 to 0.63)
    test format_()  : 0.56  (consistently 0.56)
    test format2_() : 0.58  (0.57 to 0.59)
    test percent_() : 0.34  (0.33 to 0.35)

スクリプトで文字列の連結を使用しているので、これらを実行しましたが、コストはいくらだったのでしょうか。干渉しないように、または最初または最後にパフォーマンスを向上させるために、異なる順序でそれらを実行しました。補足として、私は"%s" + ("a" * 1024)のような関数にいくつかの長い文字列ジェネレーターを投入し、通常の連結はformatメソッドと%メソッドを使用した場合のほぼ3倍(1.1対2.8)でした。私はそれが文字列とあなたが達成しようとしているものに依存していると思います。パフォーマンスが本当に重要な場合は、さまざまなことを試してテストすることをお勧めします。速度が問題にならない限り、速度より読みやすさを選択する傾向がありますが、それは私だけです。 SOコピー/ペーストが気に入らなかったので、見栄えを良くするためにすべてに8個のスペースを置く必要がありました。通常は4を使用します。

7
Cj Welborn

スタイルの決定are実際の決定、コードの保守またはデバッグを計画している場合は覚えておいてください:-) Knuthから有名な引用があります(Hoareを引用している可能性があります)。約97%の時間:早すぎる最適化はすべての悪の根源です。」

(たとえば)O(n)タスクをO(n2)タスク、私はあなたが理解しやすいと思う方と一緒に行きます。

4
John Fouhy

できる限り置換を使用します。 forループなどで文字列を作成する場合にのみ、連結を使用します。

0
Draemon