Почему значение False (0) меньше в байтах, чем True (1)?

я играл с sys ' s getsizeof() и обнаружил, что False (или 0) состоит из меньшего количества байтов, чем True (или 1). Почему так?

import sys

print("Zero: " + str(sys.getsizeof(0)))
print("One: " + str(sys.getsizeof(1)))
print("False: " + str(sys.getsizeof(False)))
print("True: " + str(sys.getsizeof(True)))

# Prints:
# Zero: 24
# One: 28
# False: 24
# True: 28

на самом деле, другие числа (также некоторые из которых состоят из более чем одной цифры) составляют 28 байт.

for n in range(0, 12):
  print(str(n) + ": " + str(sys.getsizeof(n)))

# Prints:
# 0: 24
# 1: 28
# 2: 28
# 3: 28
# 4: 28
# 5: 28
# 6: 28
# 7: 28
# 8: 28
# 9: 28
# 10: 28
# 11: 28

еще: sys.getsizeof(999999999) также 28 байт! sys.getsizeof(9999999999), однако, 32.

что происходит? Я предполагаю, что булевы True и False внутренне преобразуются в 0 и 1 соответственно, но почему ноль отличается по размеру от других, более низких чисел?

Примечание: это специфично для того, как Python представляет эти элементы, или это вообще, как цифры представлены в ОС?

1 ответов


помните, что Python int значения имеют произвольный размер. Как это работает?

Ну, в CPython,1 int представлен PyLong_Object, который имеет массив 4-байтовых кусков2, каждая из которых содержит 30 бит3 стоит число.

  • 0 не принимает никаких блоков вообще.
  • 1 - (1<<30)-1 за 1 кусок.
  • 1<<30 - (1<<60)-1 занимает 2 куски.

и так далее.

это несколько упрощено; для получения полной информации см. longintrepr.h в источнике.


в Python 2 есть два отдельных типа, называемых int и long. Ан int представлено 32-разрядным целым числом со знаком4 встроенный непосредственно в заголовок, а не массив фрагментов. А long похоже на Python 3 int.

если вы делаете тот же тест с 0L, 1L, etc., чтобы явно попросить long значения, вы получите те же результаты, что и в Python 3. Но без L суффикс, любой литерал, который вписывается в 32 бита, дает вам int, и только литералы, которые слишком велики, дам тебе longs.5 (это означает, что (1<<31)-1 это int, а 1<<31 - это 2-кусок long.)


1. В другой реализации это может оказаться неверным. IIRC, Jython делает примерно то же самое, что и CPython, но IronPython использует реализацию C# "bignum".

2. Почему 30 бит вместо 32? Главным образом потому, что реализация pow и ** может быть проще и быстрее, если можно предположить, что количество битов в двух "цифр" делится на 10.

3. Он использует C "struct hack". Технически,Py_LongObject составляет 28 байт, но никто никогда не выделяет Py_LongObject; они malloc 24, 28, 32, 36, etc. затем байты отбрасываются в Py_LongObject *.

4. На самом деле, питон int Это C long, просто чтобы все запутать. Таким образом, API C полон таких вещей, как PyInt_FromLong здесь long означает "32-битный int" и PyLong_FromSize_t здесь long означает "bignum".

5. Ранние версии Python 2.x не интегрировал int и long так же хорошо, но, надеюсь, никому больше не придется беспокоиться об этом.