Pythonのsum関数について

Pythonのsum()関数は

```python
a = [1, 2, 3, 4]
print(sum(a))
> 10
b = (1, 3, 5, 7)
> 16
c = {1: 2, 3: 4, 5: 6, 7: 8}
print(sum(c.keys()))
> 16
print(sum(c.values()))
> 20
```

など、int, flootで構成されたlist, tuple, dictionaryのkey, valueに対しての総和を計算することができる。

ところで、

```python
a = 2
b = 3
print(a + b)
> 5
a = 3
b = 4.125
print(a + b)
> 7.125
a = 'Hello'
b = 'World.'
print(a + ' ' + b)
> Hello World.
```
となることは自明の理である。(int+int, int+float,str+strはそれぞれint,float,strを返す)

しかし、

```python
a = ['Hello', ' ', 'World.']
print(sum(a))
>Traceback (most recent call last):
  File "test.py", line 2, in <module>
    print(sum(a))
TypeError: unsupported operand type(s) for +: 'int' and 'str'
```

となってしまう。エラー文を読むとintとstrを足し合わせていると出ている。listの中身にはstrしかないのに何故このようなエラー文が出力されたのか。最初に見た時の想像としては初期値0として要素を足していっているのかなと思ったが実際はどうなのか。

そこで、CPythonで書かれたsum関数の中身(https://hg.python.org/cpython/file/2.7/Python/bltinmodule.c#l2307 )を詳しく見てみると

```c
static PyObject*
builtin_sum(PyObject *self, PyObject *args)
{
    PyObject *seq;
    PyObject *result = NULL;
    PyObject *temp, *item, *iter;

    if (!PyArg_UnpackTuple(args, "sum", 1, 2, &seq, &result))
        return NULL;

    iter = PyObject_GetIter(seq);
    if (iter == NULL)
        return NULL;

    if (result == NULL) {
        result = PyInt_FromLong(0);
        if (result == NULL) {
            Py_DECREF(iter);
            return NULL;
        }
    } else {
        /* 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);
    }

...
```

簡単に中身を書くと(最初の段落)
- 1段落目...諸々の変数の定義
- 2段落目...処理のためのパラメータ取得。```PyArg_UnpackTuple```が成功した時は```True```を返すが、引数の数が今回は1未満や3以上など、様々な例外で失敗した時には```False```が返る。今回は失敗した時には```null```が返される。```seq```には第一引数(```sum```を計算したい構造体),```result```には第二引数(返り値の初期値である```start```)が入る。
- 3段落目...2段落目のパラメータ取得がうまくいった際に、```seq```のイテレータを返す。イテレータは```C```, ```C++```でよく使うが```Python```ではあまり使用されない(forで充分なのかも)。オブジェクトが反復処理不可能(```__iter__```メソッドを持たないもの)であった場合、```TypeError```が出る他、```null```が返される。
- 4段落目...```result```が```null```でない(=入力の際に2変数入力された)時は、```result```の型をチェックする。strだった場合はエラーを出す。実際に試すと

```python
print(sum([1, 2, 3], 0))
> 6
print(sum(["Hello", '', "World."], 'a'))
>Traceback (most recent call last):
  File "test.py", line 1, in <module>
    print(sum(["Hello", '', "World."], 'a'))
TypeError: sum() can't sum strings [use ''.join(seq) instead]
```
となる。また、Py_DECREFを行うことで参照カウントを減らし、メモリ開放を行う。

そうでない場合(=入力の際に1変数入力された時)は、```result```に```int```型の0を入れる。公式ドキュメント(https://docs.python.org/ja/2.7/c-api/int.html#c.PyInt_FromLong )を読むと、整数の定義を変えることで値を変えることもできるらしい？その後にも```result```の値の判定があるのかはそのせい？

ここで大事なのは4段落目で、```sum```の2引数目に値を入れていようが入れていなかろうが、初期値は```int```または```float```として扱われることになる。したがって、計算対象も```int```または```float```でなければいけない。仮説は正しかった。


文字列を計算したいなら有名ではあるが```''.join(合わせたいリスト)```とするのが良い。

```python
print(''.join(["Hello", "", "World."]))
>Hello World.
print(''.join[1, 2, 3])
>Traceback (most recent call last):
  File "Main.py", line 1, in <module>
    print(''.join([1, 2, 3]))
TypeError: sequence item 0: expected str instance, int found
```