61

Python には関数 が組み込まれておりsum、これは実質的に次のものと同等です。

def sum2(iterable, start=0):
    return start + reduce(operator.add, iterable)

文字列を除くすべてのタイプのパラメーター。数値とリストで機能します。たとえば、次のようになります。

 sum([1,2,3], 0) = sum2([1,2,3],0) = 6    #Note: 0 is the default value for start, but I include it for clarity
 sum({888:1}, 0) = sum2({888:1},0) = 888

文字列が特別に省略されたのはなぜですか?

 sum( ['foo','bar'], '') # TypeError: sum() can't sum strings [use ''.join(seq) instead]
 sum2(['foo','bar'], '') = 'foobar'

その理由については、Pythonリストでの議論を覚えているようです。そのため、説明またはそれを説明するスレッドへのリンクは問題ありません。

編集:標準的な方法は行うことであることを認識しています"".join。私の質問は、なぜ文字列に sum を使用するオプションが禁止され、たとえばリストには禁止がなかったのかということです。

編集2 :私が得たすべての良い答えを考えると、これは必要ないと思いますが、質問は: sum は、数値を含む iterable またはリストを含む iterable では機能するが、文字列を含む iterable では機能しないのはなぜですか?

4

8 に答える 8

49

Python は、文字列を「合計」することを思いとどまらせようとします。あなたはそれらに参加することになっています:

"".join(list_of_strings)

はるかに高速で、メモリの使用量がはるかに少なくなります。

簡単なベンチマーク:

$ python -m timeit -s 'import operator; strings = ["a"]*10000' 'r = reduce(operator.add, strings)'
100 loops, best of 3: 8.46 msec per loop
$ python -m timeit -s 'import operator; strings = ["a"]*10000' 'r = "".join(strings)'
1000 loops, best of 3: 296 usec per loop

編集(OPの編集に答える):文字列が明らかに「選別された」理由については、単に一般的なケースに合わせて最適化することと、ベストプラクティスを適用することの問題だと思います.''.したがって、文字列を明示的に禁止すると、これsumが初心者に指摘されます。

ところで、この制限は「永遠に」、つまりsumが組み込み関数として追加されて以来 ( rev. 32347 )

于 2010-08-19T19:15:55.930 に答える
27

実際sum(..)、適切な開始オブジェクトを使用すれば、文字列を連結するために使用できます! もちろん、ここまで行けば、"".join(..)とにかく使用するのに十分な理解が得られます..

>>> class ZeroObject(object):
...  def __add__(self, other):
...   return other
...
>>> sum(["hi", "there"], ZeroObject())
'hithere'
于 2010-08-19T20:01:06.497 に答える
17

ソースは次のとおりです。http://svn.python.org/view/python/trunk/Python/bltinmodule.c?revision=81029&view=markup

builtin_sum 関数には、次のコードがあります。

     /* reject string values for 'start' parameter */
        if (PyObject_TypeCheck(result, &PyBaseString_Type)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum strings [use ''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        Py_INCREF(result);
    }

それがあなたの答えです。

コードで明示的にチェックインされ、拒否されます。

于 2010-08-20T05:15:12.640 に答える
14

ドキュメントから:

文字列のシーケンスを連結する好ましい高速な方法は、''.join(sequence) を呼び出すことです。

文字列の操作をsum拒否することで、Python は正しい方法を使用することを奨励しています。

于 2010-08-19T19:16:24.527 に答える
11

簡単な答え:効率。

長い答え:sum関数は、部分和ごとにオブジェクトを作成する必要があります。

オブジェクトの作成に必要な時間は、そのデータのサイズに正比例するとします。合計するシーケンス内の要素の数をNで表します。

doublesは常に同じサイズであるため、sumの実行時間はO(1)×N = O(N)になります。

int(以前はlong)は任意の長さです。Mが最大のシーケンス要素の絶対値を表すとします。そのsum場合、の最悪の実行時間はlg(M)+ lg(2M)+ lg(3M)+ ... + lg(NM)= N×lg(M)+ lg(N!)= O(N log N)

str(M =最長の文字列の長さ)の場合、最悪の場合の実行時間はM + 2M + 3M + ... + NM = M×(1 + 2 + ... + N)= O (N²)です。 。

したがって、sum文字列のmingは数値のmingよりもはるかに遅くなりsumます。

str.join中間オブジェクトを割り当てません。結合された文字列を保持するのに十分な大きさのバッファを事前に割り当て、文字列データをコピーします。O(N)時間で実行され、。よりもはるかに高速ですsum

于 2010-08-20T04:46:26.957 に答える
10

理由

sum@ dan04 は、文字列の大きなリストで使用するコストについて優れた説明をしています。

strが許可されていない理由について欠けている部分sumは、非常に多くの人がsum文字列に使用しようとしていsumて、リストやタプル、その他の O(n**2) データ構造にはあまり使用されていないということです。トラップはsum、文字列の短いリストでは問題なく機能しますが、リストが巨大になる可能性がある本番環境に置かれると、パフォーマンスが低下します。これは非常に一般的なトラップであったため、この場合はダックタイピングを無視し、文字列を で使用することを許可しないという決定が下されましたsum

于 2014-04-28T20:05:53.893 に答える
4

編集:不変性に関する部分を履歴に移動しました。

基本的に、事前割り当ての問題です。などのステートメントを使用すると、

sum(["a", "b", "c", ..., ])

ステートメントと同様に機能することを期待すると、reduce生成されるコードは次のようになります

v1 = "" + "a" # must allocate v1 and set its size to len("") + len("a")
v2 = v1 + "b" # must allocate v2 and set its size to len("a") + len("b")
...
res = v10000 + "$" # must allocate res and set its size to len(v9999) + len("$")

これらの各ステップで新しい文字列が作成されますが、文字列がどんどん長くなるため、コピーのオーバーヘッドが発生する可能性があります。しかし、それはここでのポイントではないかもしれません。さらに重要なことは、各行のすべての新しい文字列を特定のサイズに割り当てるreduce必要があることです (これは、ステートメントの反復ごとに割り当てる必要があるかどうかわかりません。使用する明らかなヒューリスティックがいくつかある可能性があり、Python が割り当てる可能性があります)。再利用のためにあちこちにもう少し - しかし、いくつかのポイントで、新しい文字列が十分に大きくなるため、これでは役に立たなくなり、Python が再度割り当てる必要があり、かなりコストがかかります。

ただし、 のような専用メソッドjoinには、開始前に文字列の実際のサイズを把握する仕事があるため、理論的には、最初に一度だけ割り当ててから、その新しい文字列を埋めるだけです。これは、他のソリューションよりもはるかに安価です。

于 2010-08-19T19:26:51.197 に答える
3

理由はわかりませんが、これはうまくいきます!

import operator
def sum_of_strings(list_of_strings):
    return reduce(operator.add, list_of_strings)
于 2014-10-21T12:45:37.513 に答える