Различный размер объекта True и False в Python 3

Экспериментируя с магическими методами (в частности, __sizeof__) на разных объектах Python, я наткнулся на следующее поведение:

Python 2.7

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

Python 3.x

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

Что изменилось в Python 3, из-за чего размер True больше, чем размер False?

Связанный, такое же поведение проявляется для 0 и 1
user3483203 26.10.2018 22:41

Обратите внимание, что оценка потребления памяти с использованием sys.getsizeof и __sizeof__ (в последнем случае не учитываются накладные расходы на сборку мусора) приведет к неверным результатам, если только кто-то действительно не понимает интерпретатор Python. PyPy считает, что использование любого из них является ошибка. Например, целые числа 5 <= i <= 256 являются одиночными в CPython - [1, 1] и [1, 1, 1, 1] отличаются только двумя дополнительными указателями по размеру. В вашем случае вам нужно будет выяснить, используют ли True и 1 одну и ту же память для их стоимости.

MisterMiyagi 27.10.2018 10:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
73
2
4 671
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Это потому, что bool является подклассом int как в Python 2, так и в 3.

>>> issubclass(bool, int)
True

Но реализация int изменилась.

В Python 2 int был 32- или 64-битным, в зависимости от системы, в отличие от long произвольной длины.

В Python 3 int имеет произвольную длину - long Python 2 был переименован в int, а исходный int Python 2 был полностью удален.


В 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 по-прежнему умещается в 28 байтах на x86-64, а для 2**30 потребуется 32;

2**30 - 1 будет представлен как

<111111111111111111111111111111>

т.е. все 30 битов значений установлены в 1; 2 ** 30 понадобится больше, и у него будет внутреннее представление

<000000000000000000000000000001000000000000000000000000000000>

Что касается True, использующего байты 28 год вместо 24 - вам не о чем беспокоиться. True - это одиночка, и поэтому только 4 байта теряются в общий в любой программе Python, а не 4 при каждом использовании True.

Да, и длинные объекты просто не используют дополнительный указатель для представления 0

juanpa.arrivillaga 26.10.2018 22:48

Спасибо за ответ. Я не знал, что bool является подклассом int, но теперь это имеет смысл!

Simon Fromme 29.10.2018 18:45

Взгляните на код 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 }

Думаю, я просто не так быстро ответил должным образом, как другие :)

Kamil Niski 26.10.2018 23:00

Ответ можно найти только на полпути. Он представлен как целое число .. да? И что?

wim 26.10.2018 23:07

@wim Я не вижу необходимости объяснять это дальше, так как ребята из вышеупомянутого уже отлично поработали. Я не хочу дублировать контент. См. Их ответы.

Kamil Niski 26.10.2018 23:10

согласен, нехорошо дублировать контент в других ответах (в этих случаях обычно лучше просто удалить ответ самостоятельно). Просто пытался объяснить, почему он, возможно, был отклонен.

wim 26.10.2018 23:14

@wim Downvotes появился, когда я опубликовал первую версию ответа, вероятно, кому-то это не понравилось. Поскольку мой ответ неполный, и я не хочу дублировать лучшие ответы, следует ли мне удалить свой?

Kamil Niski 26.10.2018 23:19

Я так думаю, но решать вам. Если вы подождете, пока он наберет +3, а затем удалите, вы можете заработать значок дисциплинированный ... :)

wim 27.10.2018 00:04

Я не видел для этого кода CPython, но считаю, что это как-то связано с оптимизацией целых чисел в Python 3. Вероятно, после отказа от long некоторые оптимизации были унифицированы. int в Python 3 - это int произвольного размера - такой же, как long в Python 2. Поскольку 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

+ байты для заголовков объектов должны завершать уравнение.

Собственно, теперь в Python 3 int - это именно то, что было в Python 2 long, это был действительно int, который был «отброшен»

juanpa.arrivillaga 26.10.2018 22:48

Внутренне - абсолютно верно, я про имена, но спасибо за пояснения

Slam 26.10.2018 22:48

Действительно, в исходном коде CPython 3 это все еще longobject

juanpa.arrivillaga 26.10.2018 22:50

На самом деле это деоптимизация ... пессимизация?

Antti Haapala 26.10.2018 23:09

Наверное, нет. Возможно, я ошибаюсь, но эти накладные расходы не стоят столько, сколько два разных типа + бесшовное приведение во время выполнения. По правде говоря, вы редко используете такое огромное количество int в обычной программе Python, чтобы позаботиться об этой дополнительной памяти. Если вы это сделаете - вы должны использовать что-то вроде numpy для работы с ~ 0 накладными расходами и иметь чистый int32 / 64 - без заголовков, без перераспределения. Но время от времени переливается)

Slam 26.10.2018 23:33

И True, и False имеют longobjects в CPython:

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

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

Таким образом, вы можете сказать, что логическое значение является подклассом int, где True принимает значение 1, а False принимает значение 0. Таким образом, мы вызываем PyVarObject_HEAD_INIT со ссылкой на type в качестве параметра PyBool_Type и с ob_size в качестве значения 0 и 1 соответственно.

Теперь, после , 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];
};

Короче говоря, _longobject можно рассматривать как массив «цифр», но здесь вы должны видеть цифры не как десятичные, а как группы битов, которые, таким образом, можно складывать, умножать и т. д.

Теперь, как указано в комментарии, говорится, что:

   zero is represented by ob_size == 0.

Таким образом, если значение равно нулю, добавляются цифры нет, тогда как для небольших целых чисел (значения меньше 230 в CPython) требуется одна цифра и так далее.

В было два типа представления чисел: int (с фиксированным размером), вы могли видеть это как «одну цифру», и long, с несколькими цифрами. Поскольку bool был подклассом int, как True, так и False занимали одно и то же пространство.

Спасибо за хороший ответ! Если бы ТАК позволил мне принять несколько ответов, я бы принял и ваш! Думаю, следующим шагом к тому, чтобы стать лучшим программистом на Python, будет знакомство с важными частями исходного кода CPython ...

Simon Fromme 29.10.2018 18:48

Другие вопросы по теме