Python 3中不同对象大小的True和False


72

__sizeof__在不同的Python对象上尝试魔术方法(尤其是),我偶然发现了以下行为:

Python 2.7

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

Python 3.x

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

Python 3中发生了什么变化,使大小True大于的大小False


3
01
与之

请注意,除非真正理解Python解释器,否则使用sys.getsizeof和评估内存消耗__sizeof__(这会错过GC开销)将导致误导性结果。PyPy认为使用其中任何一个都是错误的。例如,整数5 <= i <= 256是CPython中的单例-[1, 1][1, 1, 1, 1]仅相差两个额外的指针大小。对于您的情况,您必须找出是否True1共享相同的内存以获取其价值。
MiyaGiyagi '18

Answers:


67

这是因为它是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_tso 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


是的,长对象只是不使用多余的指针来表示0
juanpa.arrivillaga 18-10-26

感谢你的回答。我不知道bool是int的子类,但是现在完全有意义!
西蒙·弗洛姆

22

这两个TrueFalselongobject小号在CPython的:

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

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

因此,您可以说布尔是a的子类。 int其中True以价值为核心1False以价值为核心0。因此,我们拨打电话到PyVarObject_HEAD_INIT与作为type参数来参考PyBool_Type,并ob_size为价值01分别。

现在,因为 ,就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)则为一位,依此类推。

,数字有两种表示形式:ints(具有固定大小),您可以将其表示为“一位数字”,而longs则具有多位数字。由于abool是的子类int,因此TrueFalse占用相同的空间。


感谢您的好答!如果SO允许我接受多个答案,我也将接受您的回答!猜想成为一名更好的Python程序员的下一步就是使自己熟悉CPython源代码的重要点……
Simon Fromme,2016年

6

我还没有看到CPython的代码,但是我认为这与Python 3中整数的优化有关longintPython 3中的int是任意大小的int,与longPython 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

+对象标头的字节应完成公式。


1
实际上,现在在Python 3中,intPython 2long就是这样,确实int是被“丢弃”了
juanpa.arrivillaga 18-10-26

内部-绝对真实,我在谈论的名字,但THX澄清
大满贯

实际上,在CPython 3源代码中,它仍然是长对象
juanpa.arrivillaga 18-10-26

1
实际上是去优化...悲观化?
安蒂·哈帕拉

1
可能不是。我可能是错的,但是这种开销并不会花费两种不同的类型+运行时的无缝转换。事实是,您很少在普通的python程序中操作如此大量的int来关心这些额外的内存。如果这样做–您应该使用numpy之类的程序以〜0的开销运行,并且具有纯int32 / 64 –没有标头,没有重新分配。但有时会溢出)
大满贯,

5

看看在CPython的代码,用于TrueFalse

在内部,它表示为整数

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 }

我认为我的回答不如其他人快:)
卡米尔·尼斯基

2
答案只有一半。它表示为整数..是吗?那又如何呢?
wim

@wim我认为不需要进一步解释,因为上述人员已经在此方面做得很好。我不想重复内容。请参考他们的答案。
卡米尔·尼斯基

同意,在其他答案中重复内容是不好的(在这种情况下,通常最好只删除自己的答案)。只是试图解释为什么它可能被否决了。
WIM

4
我认为是这样,但这取决于您。如果您等待它得到+3然后删除,您可能会获得纪律勋章... :)
wim 18-10-26
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.