さまざまなPythonオブジェクトで魔法のメソッド(特に__sizeof__
)を試してみたところ、次のような動作に遭遇しました。
Python 2.7
>>> False.__sizeof__()
24
>>> True.__sizeof__()
24
Python 3.x
>>> False.__sizeof__()
24
>>> True.__sizeof__()
28
True
のサイズをFalse
のサイズより大きくするPython 3の変更点
これは、bool
がPython 2と3の両方でint
のサブクラスであるためです。
>>> issubclass(bool, int)
True
ただし、int
実装は変更されています。
Python 2では、int
は、任意の長さのlong
ではなく、システムに応じて32ビットまたは64ビットでした。
Python 3では、int
は任意の長さです。Python 2のlong
はint
に名前が変更され、元の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バイトを使用します-心配する必要はありません。 True
はsingletonであるため、Pythonのtotalでは4バイトのみが失われますTrue
の使用ごとに4ではなく、プログラム。
True
とFalse
は両方ともCPythonでは longobject
s です:
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種類の表現、int
s(固定サイズ)があり、これは「1桁」と、複数の桁を持つlong
sとして見ることができます。 bool
はint
のサブクラスであったため、True
とFalse
の両方が同じスペースを占有していました。
True
およびFalse
の cpythonコード を見てください
内部的には整数として表されます
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 }
私はこのための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
オブジェクトヘッダーの+バイトで方程式を完成させる必要があります。