Python 3 中 True 和 False 的不同对象大小
Different object size of True and False in Python 3
在不同的 Python 对象上使用魔术方法(特别是 __sizeof__
)进行试验时,我偶然发现了以下行为:
Python 2.7
>>> False.__sizeof__()
24
>>> True.__sizeof__()
24
Python 3.x
>>> False.__sizeof__()
24
>>> True.__sizeof__()
28
Python 3 中有什么变化使 True
的大小大于 False
的大小?
因为bool
在Python2和3中都是int
的子类。
>>> issubclass(bool, int)
True
但是 int
实现发生了变化。
在 Python 2 中,int
是 32 位或 64 位的,这取决于系统,而不是任意长度的 long
.
在Python3中,int
是任意长度——Python2的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
是一个变长对象,就像一个元组——分配时,分配足够的内存来容纳所有的二进制数字需要代表它。可变部分的长度存储在对象头中。 0
不需要二进制数字(其可变长度为 0),但即使 1
也会溢出,需要额外的数字。
即0
表示为长度为0的二进制字符串:
<>
而1表示为30位的二进制串:
<000000000000000000000000000001>
Python中的默认配置使用30 bits in a uint32_t
; so 2**30 - 1
在 x86-64 上仍然适合 28 个字节,而 2**30
将需要 32 个字节;
2**30 - 1
将显示为
<111111111111111111111111111111>
即所有 30 个值位都设置为 1; 2**30 将需要更多,并且将具有内部表示
<000000000000000000000000000001000000000000000000000000000000>
至于 True
使用 28 字节而不是 24 - 你不必担心。 True
是一个 singleton 因此在任何 Python 程序中 total 只丢失 4 个字节,而不是每个程序都丢失 4 个字节True
.
的用法
查看 cpython code 的 True
和 False
内部表示为整数
PyTypeObject PyBool_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"bool",
<b>sizeof(struct _longobject)</b>,
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 */
<b>&PyLong_Type, /* tp_base */</b>
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
是 arbitrary-sized int – 与 Python 2 中的 long
相同。因为 bool
以与 new 相同的方式存储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
+ 字节 object headers 应该完成等式。
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.x int
where True
takes as value 1
, and False
takes as value 0
. We thus make a call to PyVarObject_HEAD_INIT
的子类,其中 type
参数是对 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
可以看作是 "digits" 的数组,但是您在这里看到的数字不是十进制数字,而是位组,因此可以相加、相乘等
现在正如评论中所指定的那样,它说:
zero is represented by ob_size == 0.
因此,如果值为零,则添加 no 数字,而对于小整数(CPython 中小于 230 的值), 它需要一个数字,依此类推。
在python-2.x中,数字有两种表示方式,int
s(固定大小),你可以把它看成"one digit",和long
s,有多个数字。由于 bool
是 int
的子类,因此 True
和 False
都占用相同的 space.
在不同的 Python 对象上使用魔术方法(特别是 __sizeof__
)进行试验时,我偶然发现了以下行为:
Python 2.7
>>> False.__sizeof__()
24
>>> True.__sizeof__()
24
Python 3.x
>>> False.__sizeof__()
24
>>> True.__sizeof__()
28
Python 3 中有什么变化使 True
的大小大于 False
的大小?
因为bool
在Python2和3中都是int
的子类。
>>> issubclass(bool, int)
True
但是 int
实现发生了变化。
在 Python 2 中,int
是 32 位或 64 位的,这取决于系统,而不是任意长度的 long
.
在Python3中,int
是任意长度——Python2的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
是一个变长对象,就像一个元组——分配时,分配足够的内存来容纳所有的二进制数字需要代表它。可变部分的长度存储在对象头中。 0
不需要二进制数字(其可变长度为 0),但即使 1
也会溢出,需要额外的数字。
即0
表示为长度为0的二进制字符串:
<>
而1表示为30位的二进制串:
<000000000000000000000000000001>
Python中的默认配置使用30 bits in a uint32_t
; so 2**30 - 1
在 x86-64 上仍然适合 28 个字节,而 2**30
将需要 32 个字节;
2**30 - 1
将显示为
<111111111111111111111111111111>
即所有 30 个值位都设置为 1; 2**30 将需要更多,并且将具有内部表示
<000000000000000000000000000001000000000000000000000000000000>
至于 True
使用 28 字节而不是 24 - 你不必担心。 True
是一个 singleton 因此在任何 Python 程序中 total 只丢失 4 个字节,而不是每个程序都丢失 4 个字节True
.
查看 cpython code 的 True
和 False
内部表示为整数
PyTypeObject PyBool_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"bool",
<b>sizeof(struct _longobject)</b>,
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 */
<b>&PyLong_Type, /* tp_base */</b>
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
是 arbitrary-sized int – 与 Python 2 中的 long
相同。因为 bool
以与 new 相同的方式存储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
+ 字节 object headers 应该完成等式。
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.x int
where True
takes as value 1
, and False
takes as value 0
. We thus make a call to PyVarObject_HEAD_INIT
的子类,其中 type
参数是对 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
可以看作是 "digits" 的数组,但是您在这里看到的数字不是十进制数字,而是位组,因此可以相加、相乘等
现在正如评论中所指定的那样,它说:
zero is represented by ob_size == 0.
因此,如果值为零,则添加 no 数字,而对于小整数(CPython 中小于 230 的值), 它需要一个数字,依此类推。
在python-2.x中,数字有两种表示方式,int
s(固定大小),你可以把它看成"one digit",和long
s,有多个数字。由于 bool
是 int
的子类,因此 True
和 False
都占用相同的 space.