Почему значение 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
, и только литералы, которые слишком велики, дам тебе long
s.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
так же хорошо, но, надеюсь, никому больше не придется беспокоиться об этом.