__sizeof__
在不同的Python对象上尝试魔术方法(尤其是),我偶然发现了以下行为:
Python 2.7
>>> False.__sizeof__()
24
>>> True.__sizeof__()
24
Python 3.x
>>> False.__sizeof__()
24
>>> True.__sizeof__()
28
Python 3中发生了什么变化,使大小True
大于的大小False
?
__sizeof__
在不同的Python对象上尝试魔术方法(尤其是),我偶然发现了以下行为:
Python 2.7
>>> False.__sizeof__()
24
>>> True.__sizeof__()
24
Python 3.x
>>> False.__sizeof__()
24
>>> True.__sizeof__()
28
Python 3中发生了什么变化,使大小True
大于的大小False
?
sys.getsizeof
和评估内存消耗__sizeof__
(这会错过GC开销)将导致误导性结果。PyPy认为使用其中任何一个都是错误的。例如,整数5 <= i <= 256是CPython中的单例-[1, 1]
且[1, 1, 1, 1]
仅相差两个额外的指针大小。对于您的情况,您必须找出是否True
和1
共享相同的内存以获取其价值。
Answers:
这是因为它是Python 2和3bool
的子类int
。
>>> issubclass(bool, int)
True
但是int
实现方式已经改变。
在Python 2中,int
是32位或64位的那个,具体取决于系统,而不是任意长度long
。
在Python 3中,它int
是任意长度long
的-Python 2的名称已重命名为int
,而原始的Python 2则int
完全删除了。
在Python 2中,对于长对象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位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
是一个单例,因此在任何Python程序中总共仅丢失4个字节,而不是每次使用4个字节True
。
这两个True
和False
是longobject
小号在CPython的:
struct _longobject _Py_FalseStruct = {
PyVarObject_HEAD_INIT(&PyBool_Type, 0)
{ 0 }
};
struct _longobject _Py_TrueStruct = {
PyVarObject_HEAD_INIT(&PyBool_Type, 1)
{ 1 }
};
因此,您可以说布尔是a的子类。 python-3.x int
其中True
以价值为核心1
,False
以价值为核心0
。因此,我们拨打电话到PyVarObject_HEAD_INIT
与作为type
参数来参考PyBool_Type
,并ob_size
为价值0
和1
分别。
现在,因为 python-3.x,就long
不再存在:它们已经合并,并且int
对象将根据数字的大小采用不同的值。
如果我们检查longlobject
类型的源代码,则会看到:
/* 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];
};
长话短说,an_longobject
可以看作是“数字”的数组,但是在这里您应该看到数字不是十进制数字,而是可以加,乘等的位组。
现在,如注释中所指定,它说:
zero is represented by ob_size == 0.
因此,如果该值为零,则不添加数字,而对于较小的整数(在CPython中,值小于2 30)则为一位,依此类推。
在 python-2.x,数字有两种表示形式:int
s(具有固定大小),您可以将其表示为“一位数字”,而long
s则具有多位数字。由于abool
是的子类int
,因此True
和False
占用相同的空间。
我还没有看到CPython的代码,但是我认为这与Python 3中整数的优化有关long
。int
Python 3中的int是任意大小的int,与long
Python 2中的int相同。由于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
+对象标头的字节应完成公式。
int
Python 2long
就是这样,确实int
是被“丢弃”了
看看在CPython的代码,用于True
和False
在内部,它表示为整数
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 }
0
1