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 对象 1L0L:

的行为完全相同
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_tso 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 codeTrueFalse

内部表示为整数

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 应该完成等式。

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 }
};

因此,您可以说布尔值是 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 的值是 01 分别。

自从 以来,不再有 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 的值), 它需要一个数字,依此类推。

中,数字有两种表示方式,ints(固定大小),你可以把它看成"one digit",和longs,有多个数字。由于 boolint 的子类,因此 TrueFalse 都占用相同的 space.