0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Pythonのsum関数について

Last updated at Posted at 2021-04-24

Pythonのsum()関数は

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に対しての総和を計算することができる。

ところで、

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を返す)

しかし、

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 )を詳しく見てみると

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段落目...resultnullでない(=入力の際に2変数入力された)時は、resultの型をチェックする。strだった場合はエラーを出す。実際に試すと
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変数入力された時)は、resultint型の0を入れる。公式ドキュメント(https://docs.python.org/ja/2.7/c-api/int.html#c.PyInt_FromLong )を読むと、整数の定義を変えることで値を変えることもできるらしい?その後にもresultの値の判定があるのかはそのせい?

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

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

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
0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?