web-dev-qa-db-ja.com

Python 3のTrueとFalseの異なるオブジェクトサイズ

さまざまなPythonオブジェクトで魔法のメソッド(特に__sizeof__)を試してみたところ、次のような動作に遭遇しました。

Python 2.7

>>> False.__sizeof__()
24
>>> True.__sizeof__()
24

Python 3.x

>>> False.__sizeof__()
24
>>> True.__sizeof__()
28

TrueのサイズをFalseのサイズより大きくするPython 3の変更点

66
Simon Fromme

これは、boolがPython 2と3の両方でintのサブクラスであるためです。

>>> issubclass(bool, int)
True

ただし、int実装は変更されています。

Python 2では、intは、任意の長さのlongではなく、システムに応じて32ビットまたは64ビットでした。

Python 3では、intは任意の長さです。Python 2のlongintに名前が変更され、元のPython 2 intは完全に削除されました。


Python 2では、longオブジェクト1Lおよび0Lに対してまったく同じ動作が得られます。

Python 2.7.15rc1 (default, Apr 15 2018, 21:51:34) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getsizeof(1L)
28
>>> sys.getsizeof(0L)
24

long/Python 3 intは、タプルのように可変長のオブジェクトです。割り当てられると、それを表現するために必要なすべての2進数を保持するのに十分なメモリが割り当てられます。可変部分の長さはオブジェクトヘッドに格納されます。 0は2進数を必要としません(その可変長は0です)が、1でもあふれて、余分な数字が必要です。

つまり0は、長さ0のバイナリ文字列として表されます。

<>

1は30ビットのバイナリ文字列として表されます:

<000000000000000000000000000001>

Pythonのデフォルト構成では、 uint32_tの30ビット を使用します。 so 2**30 - 1は依然としてx86-64の28バイトに収まり、2**30は32バイトを必要とします。

2**30 - 1は次のように表示されます

<111111111111111111111111111111>

すなわち、1に設定された30個の値ビットすべて。 2 ** 30はさらに必要であり、内部表現を持ちます。

<000000000000000000000000000001000000000000000000000000000000>

Trueに関しては、28バイトの代わりに28バイトを使用します-心配する必要はありません。 Truesingletonであるため、Pythonのtotalでは4バイトのみが失われますTrueの使用ごとに4ではなく、プログラム。

61
Antti Haapala

TrueFalseは両方ともCPythonでは longobjects です:

struct _longobject _Py_FalseStruct = {
    PyVarObject_HEAD_INIT(&PyBool_Type, 0)
    { 0 }
};

struct _longobject _Py_TrueStruct = {
    PyVarObject_HEAD_INIT(&PyBool_Type, 1)
    { 1 }
};

したがって、ブール値は python-3.xintのサブクラスであり、ここでTrueは値1を取り、Falseは値0を取ります。したがって、typeパラメーターとしてPyVarObject_HEAD_INITへの参照を使用し、それぞれPyBool_Typeおよびob_sizeとして0を使用して、 1 を呼び出します。

python-3.x なので、longはもうありません。これらはマージされており、intオブジェクトは、数値のサイズに応じて異なる値を取ります。

longlobject type のソースコードを調べると、次のように表示されます。

/* Long integer representation.
   The absolute value of a number is equal to
        SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(SHIFT*i)
   Negative numbers are represented with ob_size < 0;
   zero is represented by ob_size == 0.
   In a normalized number, ob_digit[abs(ob_size)-1] (the most significant
   digit) is never zero. Also, in all cases, for all valid i,
        0 <= ob_digit[i] <= MASK.
   The allocation function takes care of allocating extra memory
   so that ob_digit[0] ... ob_digit[abs(ob_size)-1] are actually available.
   CAUTION: Generic code manipulating subtypes of PyVarObject has to
   aware that ints abuse ob_size's sign bit.
*/

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

長い話を短くするために、_longobjectは「数字」の配列として見ることができますが、ここでは数字を10進数字としてではなく、追加、乗算などが可能なビットのグループとして見る必要があります。

コメントで指定されているとおり、次のようになります。

   zero is represented by ob_size == 0.

したがって、値がゼロの場合、no数字が追加されますが、小さな整数の場合(2未満の値)30 CPython)では、1桁の数字が必要です。

python-2.x には、数値の2種類の表現、ints(固定サイズ)があり、これは「1桁」と、複数の桁を持つlongsとして見ることができます。 boolintのサブクラスであったため、TrueFalseの両方が同じスペースを占有していました。

19

TrueおよびFalsecpythonコード を見てください

内部的には整数として表されます

PyTypeObject PyBool_Type = {
        PyVarObject_HEAD_INIT(&PyType_Type, 0)
        "bool",
        sizeof(struct _longobject),
        0,
        0,                                          /* tp_dealloc */
        0,                                          /* tp_print */
        0,                                          /* tp_getattr */
        0,                                          /* tp_setattr */
        0,                                          /* tp_reserved */
        bool_repr,                                  /* tp_repr */
        &bool_as_number,                            /* tp_as_number */
        0,                                          /* tp_as_sequence */
        0,                                          /* tp_as_mapping */
        0,                                          /* tp_hash */
        0,                                          /* tp_call */
        bool_repr,                                  /* tp_str */
        0,                                          /* tp_getattro */
        0,                                          /* tp_setattro */
        0,                                          /* tp_as_buffer */
        Py_TPFLAGS_DEFAULT,                         /* tp_flags */
        bool_doc,                                   /* tp_doc */
        0,                                          /* tp_traverse */
        0,                                          /* tp_clear */
        0,                                          /* tp_richcompare */
        0,                                          /* tp_weaklistoffset */
        0,                                          /* tp_iter */
        0,                                          /* tp_iternext */
        0,                                          /* tp_methods */
        0,                                          /* tp_members */
        0,                                          /* tp_getset */
        &PyLong_Type,                               /* tp_base */
        0,                                          /* tp_dict */
        0,                                          /* tp_descr_get */
        0,                                          /* tp_descr_set */
        0,                                          /* tp_dictoffset */
        0,                                          /* tp_init */
        0,                                          /* tp_alloc */
        bool_new,                                   /* tp_new */
    };

    /* The objects representing bool values False and True */

    struct _longobject _Py_FalseStruct = {
        PyVarObject_HEAD_INIT(&PyBool_Type, 0)
        { 0 }
    };

    struct _longobject _Py_TrueStruct = {
        PyVarObject_HEAD_INIT(&PyBool_Type, 1)
    { 1 }
6
Kamil Niski

私はこのためのCPythonコードを見ていませんが、これはPython 3での整数の最適化と関係があると思います。おそらく、longが削除されたため、いくつかの最適化が統一されました。 Python 3のintは、任意のサイズのint – Python in2のlongと同じです。boolは、新しいintと同じ方法で格納されるため、両方に影響します。

興味深い部分:

>>> (0).__sizeof__()
24

>>> (1).__sizeof__()  # Here one more "block" is allocated
28

>>> (2**30-1).__sizeof__()  # This is the maximum integer size fitting into 28
28

オブジェクトヘッダーの+バイトで方程式を完成させる必要があります。

6
Slam