web-dev-qa-db-ja.com

Python sum、なぜ文字列ではないのですか?

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。私の質問は、文字列に和を使用するオプションが禁止された理由であり、リストなどには禁止がありませんでした。

編集2:私が得たすべての良い答えを考えると、これは必要ではないと思いますが、質問は次のとおりです:リストを含むが、文字列を含むイテラブルを含まない?

61

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

49
rbp

適切な開始オブジェクトを使用する場合、実際にはsum(..)を使用して文字列を連結できます!もちろん、ここまで行けば、"".join(..)を使用するのに十分なことを既に理解していることになります。

>>> class ZeroObject(object):
...  def __add__(self, other):
...   return other
...
>>> sum(["hi", "there"], ZeroObject())
'hithere'
27
u0b34a0f6ae

ここにソースがあります: 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);
    }

だから..それはあなたの答えです。

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

17
HS.

ドキュメント から:

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

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

14
unutbu

簡単な答え:効率。

長い答え: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²)

したがって、summing文字列はsumming番号よりもはるかに遅くなります。

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

11
dan04

理由

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

strsumに許可されない理由に関する欠落部分は、多くの人が文字列にsumを使用しようとしていたことであり、リストとタプルおよびその他のO(n ** 2)データ構造にsumを使用する人は多くありません。トラップは、sumが文字列の短いリストに対しては正常に機能しますが、リストが巨大になる可能性のある本番環境に置かれ、パフォーマンスが低下してクロールすることです。これは、この例ではアヒルのタイピングを無視し、文字列をsumで使用することを許可しないという決定が下されたという一般的なトラップでした。

10
Ethan Furman

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

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

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("$")

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

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

4
Debilski

理由はわかりませんが、これは機能します!

import operator
def sum_of_strings(list_of_strings):
    return reduce(operator.add, list_of_strings)
3