気ままなタンス*プログラミングなどのノートブック

プログラミングやRPGツクール、DTM、VOCALOIDについてのんびり書きます。

【Python】変数とオブジェクト(メモ)

タスクリストに書いた「Python本のななめ読み」について。rinnegrid.hatenablog.com

オライリー本(初めてのPython)をのんびり読んでいた。

1. Pythonの変数は型を持たない
2. 型情報はオブジェクトが保有する
3. 「==」で同等、 「in」で同一を表現する

Pythonの変数

変数は、サーチテーブルのエントリとオブジェクトへのリンクのスペースを組み合わせたもの。
型自体が変数の情報を持つわけではない。

Pythonのオブジェクト

オブジェクトは、対応する値と型についての情報を格納するためのメモリ領域
リファレンスカウンタを保持

a = "str"
a = 1
a = [1, 2, 3]

# aには、それぞれ、文字列、数値、リストオブジェクトへのリンクが代入される
変数 オブジェクト
サーチテーブルへのエントリ 型情報
オブジェクトへのリンク リファレンスカウンタ


サーチテーブルという表現が出てきたが、何のことを表しているのかいまいち不明。

Cのソースを追ってみる
たくさんあってよくわからないので、それっぽい「object.c」からたどる。
PyObject_InitVar:変数の初期設定っぽいけど、全体像が見えてないので、勝手な推測で進めていった。

_Py_NewReference((PyObject *)op);っていうところで、
PyVarObject(変数オブジェクト?)を_Py_NewReferenceで処理してる。
_Py_NewReference(inc_count)では、_Py_AddToAllObjects関数で受け取ったPyTypeObjectをrefchainに追加している(?)。

これがサーチテーブルの追加なのだろうか?発行元に質問投げてみるのが早いかもしれない。

// Objects/object.c
// 231行目あたり
PyVarObject *
PyObject_InitVar(PyVarObject *op, PyTypeObject *tp, Py_ssize_t size)
{
    if (op == NULL)
        return (PyVarObject *) PyErr_NoMemory();
    /* Any changes should be reflected in PyObject_INIT_VAR */
    op->ob_size = size;
    Py_TYPE(op) = tp;
    _Py_NewReference((PyObject *)op);
    return op;
}

// Py_TYPEマクロ
// Include/object.h
/// 117行目あたり
#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)

// _Py_NewReferenceマクロ
// Include/object.h
// 751行目あたり
#define _Py_NewReference(op) (                          \
    _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA         \
    _Py_INC_REFTOTAL  _Py_REF_DEBUG_COMMA               \
    Py_REFCNT(op) = 1)



// _Py_INC_TPALLOCSマクロ、_Py_COUNT_ALLOCS_COMMAマクロ、_Py_INC_REFTOTALマクロ
// Include/object.h
// 724行目あたり
// COUNT_ALLOCSが定義されていれば、
// 関数やカンマに置換されるが、それ以外はブランク

#ifdef COUNT_ALLOCS
PyAPI_FUNC(void) inc_count(PyTypeObject *);
PyAPI_FUNC(void) dec_count(PyTypeObject *);
#define _Py_INC_TPALLOCS(OP)    inc_count(Py_TYPE(OP))
#define _Py_INC_TPFREES(OP)     dec_count(Py_TYPE(OP))
#define _Py_DEC_TPFREES(OP)     Py_TYPE(OP)->tp_frees--
#define _Py_COUNT_ALLOCS_COMMA  ,
#else
#define _Py_INC_TPALLOCS(OP)
#define _Py_INC_TPFREES(OP)
#define _Py_DEC_TPFREES(OP)
#define _Py_COUNT_ALLOCS_COMMA
#endif /* COUNT_ALLOCS */

// Py_REFCNTマクロ
// Include/object.h
// 116行目あたり
#define Py_REFCNT(ob)           (((PyObject*)(ob))->ob_refcnt)


// Py_INC_REFTOTALマクロ、_Py_REF_DEBUG_COMMAマクロ
// Include/object.h
// 703行目あたり

// Py_REF_DEBUGが定義されている場合、_Py_RefTotalをインクリメント


#ifdef Py_REF_DEBUG
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
PyAPI_FUNC(void) _Py_NegativeRefcount(const char *fname,
                                            int lineno, PyObject *op);
PyAPI_FUNC(PyObject *) _PyDict_Dummy(void);
PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
#define _Py_INC_REFTOTAL        _Py_RefTotal++
#define _Py_DEC_REFTOTAL        _Py_RefTotal--
#define _Py_REF_DEBUG_COMMA     ,
#define _Py_CHECK_REFCNT(OP)                                    \
{       if (((PyObject*)OP)->ob_refcnt < 0)                             \
                _Py_NegativeRefcount(__FILE__, __LINE__,        \
                                     (PyObject *)(OP));         \
}
#else
#define _Py_INC_REFTOTAL
#define _Py_DEC_REFTOTAL
#define _Py_REF_DEBUG_COMMA
#define _Py_CHECK_REFCNT(OP)    /* a semicolon */;
#endif /* Py_REF_DEBUG */

// inc_count関数
// Objects/object.c
// 141行目あたり
void
inc_count(PyTypeObject *tp)
{
    if (tp->tp_next == NULL && tp->tp_prev == NULL) {
        /* first time; insert in linked list */
        if (tp->tp_next != NULL) /* sanity check */
            Py_FatalError("XXX inc_count sanity check");
        if (type_list)
            type_list->tp_prev = tp;
        tp->tp_next = type_list;
        /* Note that as of Python 2.2, heap-allocated type objects
         * can go away, but this code requires that they stay alive
         * until program exit.  That's why we're careful with
         * refcounts here.  type_list gets a new reference to tp,
         * while ownership of the reference type_list used to hold
         * (if any) was transferred to tp->tp_next in the line above.
         * tp is thus effectively immortal after this.
         */
        Py_INCREF(tp);
        type_list = tp;
#ifdef Py_TRACE_REFS
        /* Also insert in the doubly-linked list of all objects,
         * if not already there.
         */
        _Py_AddToAllObjects((PyObject *)tp, 0);
#endif
    }
    tp->tp_allocs++;
    if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc)
        tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees;
}


// Objects/object.c
// 82行目あたり
static PyTypeObject *type_list;


// Objects/object.c
// 43行目あたり
#ifdef Py_TRACE_REFS
/* Head of circular doubly-linked list of all objects.  These are linked
 * together via the _ob_prev and _ob_next members of a PyObject, which
 * exist only in a Py_TRACE_REFS build.
 */
static PyObject refchain = {&refchain, &refchain};

/* Insert op at the front of the list of all objects.  If force is true,
 * op is added even if _ob_prev and _ob_next are non-NULL already.  If
 * force is false amd _ob_prev or _ob_next are non-NULL, do nothing.
 * force should be true if and only if op points to freshly allocated,
 * uninitialized memory, or you've unlinked op from the list and are
 * relinking it into the front.
 * Note that objects are normally added to the list via _Py_NewReference,
 * which is called by PyObject_Init.  Not all objects are initialized that
 * way, though; exceptions include statically allocated type objects, and
 * statically allocated singletons (like Py_True and Py_None).
 */
void
_Py_AddToAllObjects(PyObject *op, int force)
{
#ifdef  Py_DEBUG
    if (!force) {
        /* If it's initialized memory, op must be in or out of
         * the list unambiguously.
         */
        assert((op->_ob_prev == NULL) == (op->_ob_next == NULL));
    }
#endif
    if (force || op->_ob_prev == NULL) {
        op->_ob_next = refchain._ob_next;
        op->_ob_prev = &refchain;
        refchain._ob_next->_ob_prev = op;
        refchain._ob_next = op;
    }
}
#endif  /* Py_TRACE_REFS */