CPython で名前束縛を行なっているところを探してる...

知りたいのは

  • 実際に名前束縛を行なっているコード
  • どのようにメモリを確保しているか.. malloc しているか..

これを読んでおこう

https://qiita.com/DOSS_INCREMENT/items/36629e1f4044de4742a1
https://qiita.com/tans/items/87f140e4a763bd11c185
Python のソースを読んでみる

◯ 型とオブジェクト

Pythonの内部構造::PyObject ― CPythonの実装から内部に迫る

最初に次の2つをごっちゃにして混乱した。
PyObject ... インスタンスオブジェクトに該当
PyTypeObject ... クラスオブジェクトに該当

CPython の中では int クラスのインスタンスオブジェクト、例えば 1 に対して CPython の型として PyLongObject があり、Python の型として PyLongObject がある。

PyLongObject

// 
typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */

Include/longobject.h


struct _longobject {
    PyObject_VAR_HEAD
    digit ob_digit[1];
};

Include/longintrepr.h


短絡的に digit ob_digit[1]; に値が格納されているのかなと思ったけど、話はそう簡単ではなさそう..。てか 変数名としての digit ob_digit[1]; って何だ...。 https://stackoverflow.com/questions/23016610/why-do-ints-require-three-times-as-much-memory-in-python


あと、いったい、いつ型 PyLong_Type を指定してるのかなと思ったらクラスを生成するときだった。構造体を定義するときに typdef struct するときに指定できたらいいんだろうけど、 C にはそんな機能なさそう...。

int は immutable だから重複をチェックする様なコードがあるのかなと思ったけど、ここでは見つけられず。malloc とかよくわからないから読んだら勉強になりそう。

PyLongObject *
_PyLong_New(Py_ssize_t size)
{
    PyLongObject *result;
    /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
       sizeof(digit)*size.  Previous incarnations of this code used
       sizeof(PyVarObject) instead of the offsetof, but this risks being
       incorrect in the presence of padding between the PyVarObject header
       and the digits. */
    if (size > (Py_ssize_t)MAX_LONG_DIGITS) {
        PyErr_SetString(PyExc_OverflowError,
                        "too many digits in integer");
        return NULL;
    }
    result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +
                             size*sizeof(digit));
    if (!result) {
        PyErr_NoMemory();
        return NULL;
    }
    // ここで型を指定して生成している...
    return (PyLongObject*)PyObject_INIT_VAR(result, &PyLong_Type, size);
}

Objects/longobject.c

PyLong_Type

これは型を宣言 struct typedef してるんじゃなくて、値を代入 = している。頭の中が Python になってるから 0 を埋める意味が一瞬わからなかった。事前に、各クラスで必要となりうる全ての関数を定めておかないといけないのか...。しかし、このコードだと関数を一つ追加しますってなったときに辛いんじゃないのかなとも思うけど、個々にメンバに代入するよりも、きっといいんやろうな、なんで何やろうか。

// int クラスのクラスオブジェクトのインスタンス
// 構造体の初期化に慣れてなくて、
// 何で列挙しているだけなのかわからなかった... orz
PyTypeObject PyLong_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "int",                                      /* tp_name */
    offsetof(PyLongObject, ob_digit),           /* tp_basicsize */
    sizeof(digit),                              /* tp_itemsize */
    long_dealloc,                               /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    long_to_decimal_string,                     /* tp_repr */
    &long_as_number,                            /* tp_as_number */

object/longobject.c

◯ 字句解析

こいつが実際の字句解析を行なっている。
https://github.com/python/cpython/blob/master/Parser/tokenizer.c
字句解析

主な関数は多分こいつ

static int
tok_get(struct tok_state *tok, char **p_start, char **p_end)

構文解析

https://github.com/python/cpython/blob/master/Parser/parser.c
構文解析

◯ 組込モジュール

https://github.com/python/cpython/tree/master/Modules
https://github.com/python/cpython/tree/master/Lib

◯ 組込クラス

intobject.c がない。longobject.c に統合されたのかな...
string は stringlib に収まっていそう。
https://github.com/python/cpython/tree/master/Objects