読者です 読者をやめる 読者になる 読者になる

何かを書き留める何か

数学や読んだ本について書く何かです。最近は社会人として生き残りの術を学ぶ日々です。

Pythonの組み込み函数sumについて

Python

Pythonで有限体を実装しようと思い立ち、j2kun/finite-fields · GitHubを参考に体上の多項式環を実装しようとしていた。 多項式の和で組み込みのsumを利用しようとしたらTypeErrorが生じた。原因を調べたところ、sum内で組み込み型intと自作の体のクラスと__add__をしようとしてはまっていることがわかった。自作の体のクラスにはきちんと__add__を定義しているのに…と思ってsumの実装を調べてみた。

組み込みのsumのコードの本質的と思われる箇所の抜粋は以下の通りである。

static PyObject *
builtin_sum_impl(PyModuleDef *module, PyObject *iterable, PyObject *start)
{
    PyObject *result = start;
    PyObject *temp, *item, *iter;

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

    if (result == NULL) {
        result = PyLong_FromLong(0);
        if (result == NULL) {
            Py_DECREF(iter);
            return NULL;
        }
    } else {
        /* reject string values for 'start' parameter */
        if (PyUnicode_Check(result)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum strings [use ''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        if (PyBytes_Check(result)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum bytes [use b''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        if (PyByteArray_Check(result)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum bytearray [use b''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        Py_INCREF(result);
    }

    for(;;) {
        item = PyIter_Next(iter);
        if (item == NULL) {
            /* error, or end-of-sequence */
            if (PyErr_Occurred()) {
                Py_DECREF(result);
                result = NULL;
            }
            break;
        }

        temp = PyNumber_Add(result, item);
        Py_DECREF(result);
        Py_DECREF(item);
        result = temp;
        if (result == NULL)
            break;
    }
    Py_DECREF(iter);
    return result;
}

わかったこととして、

    if (result == NULL) {
        result = PyLong_FromLong(0);
        if (result == NULL) {
            Py_DECREF(iter);
            return NULL;
        }

の部分でsum(iterable, start)startに何も入れないとint型の0startに入り、 自作の型と__add__をしようとしてエラーになった、と。 そのため、startに自作の有限体の零元を入れることで期待通りの動作をした。

また、sum関数は文字列を拒絶する旨がヘルプに書いてあるが、引用したコードを見るとバイト列も拒絶していることがわかる。 普段はPythonの内部ソースなんて気にしていないが、こう読んでみると面白い。

なお、ソースコードにはArgument Clinic How-To — Python 3.4.3 ドキュメントにあるツールが使われた痕跡があった。 Argument Clinicは"Monty Python's Flying Circus"の第3シーズンエピソード3の最後のスケッチが由来だと思われる。 "I'd like to have an argument please." という不思議な台詞から始まる。 案内された部屋に入ると罵声を浴びせられたり、謎の水掛け論の応酬が始まったり、仕舞いには突然ハンマーで殴られて「ここは頭を殴られる教室だ」と続き、そして警察が…とチャップマンとクリーズのケンブリッジ組が書いたと思われる会話主体の面白いスケッチである。