Добавить в корзинуПозвонить
Найти в Дзене
Анастасия Софт

💾 Битовые сдвиги и экономия памяти: трюки системных программистов в Python

Вам когда-нибудь говорили, что Python — это высокоуровневый язык, и тут не место всяким там "битовым выкрутасам"?
А вот и нет. Внутри каждого "питониста" рано или поздно просыпается системщик, который хочет: И вот тут на сцену выходят битовые сдвиги и упаковка данных. Это просто способ "пошевелить" биты числа: ОперацияЧто делаетПример (n = 4)n << 1Сдвиг влево4 << 1 = 8n >> 1Сдвиг вправо4 >> 1 = 2 Почему? Потому что в двоичном: А теперь — к мясу. Примеры! Допустим, у вас есть объект с такими свойствами: ФлагБит №VISIBLE0SELECTED1ACTIVE2LOCKED3 Вместо 4 отдельных bool, можно просто: VISIBLE = 1 << 0 # 0001
SELECTED = 1 << 1 # 0010
ACTIVE = 1 << 2 # 0100
LOCKED = 1 << 3 # 1000
flags = 0
flags |= VISIBLE | ACTIVE # включаем два флага
# Проверка
print(bool(flags & VISIBLE)) # True
print(bool(flags & SELECTED)) # False 🎯 Плюс: вместо 4 переменных — одно число!
🧠 Память: 1 int вместо 4 bool (которые в CPython весят по 28 байт каждый!). У вас есть два числа: x и y, каждое от
Оглавление

(или как уместить целый мир в одном байте)

Битовые сдвиги и экономия памяти: трюки системных программистов в Python
Битовые сдвиги и экономия памяти: трюки системных программистов в Python

Вам когда-нибудь говорили, что Python — это высокоуровневый язык, и тут не место всяким там "битовым выкрутасам"?

А вот и нет. Внутри каждого "питониста" рано или поздно просыпается
системщик, который хочет:

  • экономить память,
  • ускорить вычисления,
  • чувствовать себя ближе к железу,
  • и просто делать красиво.

И вот тут на сцену выходят битовые сдвиги и упаковка данных.

🧠 Что такое битовый сдвиг?

Это просто способ "пошевелить" биты числа:

ОперацияЧто делаетПример (n = 4)n << 1Сдвиг влево4 << 1 = 8n >> 1Сдвиг вправо4 >> 1 = 2

Почему? Потому что в двоичном:

  • 4 = 0100
  • 4 << 1 = 1000 = 8
  • 4 >> 1 = 0010 = 2

📦 Где это используется?

  • Упаковка нескольких значений в одном числе
  • Флаги и состояния
  • Быстрая арифметика (x * 2 == x << 1)
  • Сериализация и бинарные протоколы
  • Игровые движки и сетевой код

А теперь — к мясу. Примеры!

✅ Задача 1: Храним несколько флагов в одном числе

Допустим, у вас есть объект с такими свойствами:

ФлагБит №VISIBLE0SELECTED1ACTIVE2LOCKED3

Вместо 4 отдельных bool, можно просто:

VISIBLE = 1 << 0 # 0001
SELECTED = 1 << 1 # 0010
ACTIVE = 1 << 2 # 0100
LOCKED = 1 << 3 # 1000

flags = 0
flags |= VISIBLE | ACTIVE # включаем два флага

# Проверка
print(bool(flags & VISIBLE)) # True
print(bool(flags & SELECTED)) # False

🎯 Плюс: вместо 4 переменных — одно число!

🧠
Память: 1 int вместо 4 bool (которые в CPython весят по 28 байт каждый!).

✅ Задача 2: Упаковываем два маленьких числа в один байт

У вас есть два числа: x и y, каждое от 0 до 15 (4 бита). Хотите сэкономить место.

def pack(x, y):
return (x << 4) | y # старшие 4 бита — x, младшие — y

def unpack(value):
x = value >> 4 # достаём старшие
y = value & 0b1111 # достаём младшие
return x, y

Пример:

z = pack(9, 12)
print(bin(z)) # 0b10011100

x, y = unpack(z)
print(x, y) # 9 12

📦 Используется в графике, микроконтроллерах и в протоколах, где каждый бит на счету.

✅ Задача 3: Ускоряем умножение и деление

Битовые сдвиги могут быть быстрее, чем * или /. Это не всегда актуально в Python (где всё через объекты), но принцип полезен.

x = 10
print(x << 1) # x * 2 → 20
print(x << 2) # x * 4 → 40
print(x >> 1) # x // 2 → 5

🏎️ В низкоуровневых языках такие трюки — стандарт для оптимизации, особенно в циклах или численных вычислениях.

✅ Задача 4: Калькулятор битовых флагов конфигурации

Допустим, вы пишете движок игры. У вас есть "существа", у каждого из которых может быть множество статусов:

CAN_JUMP = 1 << 0
CAN_FLY = 1 << 1
IS_BOSS = 1 << 2
INVISIBLE = 1 << 3

def describe(flags):
if flags & CAN_JUMP:
print("Может прыгать")
if flags & CAN_FLY:
print("Умеет летать")
if flags & IS_BOSS:
print("Это босс")
if flags & INVISIBLE:
print("Невидимка!")

Использование:

orc = CAN_JUMP | IS_BOSS
describe(orc)
# → Может прыгать
# → Это босс

👾 В играх и графике флаги — обязательная тема. Вместо dict, list, set — просто одно число!

✅ Задача 5: Сжимаем булевы массивы

Представьте, у вас массив из 32 True/False. Хотите хранить это всего в одном числе, а не в списке.

def pack_bools(bools):
result = 0
for i, val in enumerate(bools):
if val:
result |= (1 << i) # ставим бит
return result

def unpack_bools(bits, size=32):
return [(bits >> i) & 1 == 1 for i in range(size)]

Пример:

flags = [False] * 32
flags[0] = True
flags[3] = True
flags[31] = True

packed = pack_bools(flags)
print(bin(packed)) # длинная строка с тремя 1

unpacked = unpack_bools(packed)
print(unpacked[:5]) # [True, False, False, True, False]

🔬 Иногда массив булевых значений можно сжать в 32 раза, используя один int.

🧠 Бонус: Почему это реально экономит память?

В Python:

sys.getsizeof(True) → 28 байт
sys.getsizeof(False) → 28 байт
sys.getsizeof(0) → 24 байта

А int с 32 битами (например, 4294967295) тоже занимает 28 байт — и может хранить 32 булевых значения.

📉 Это экономия примерно в 28 раз на массиве флагов. Особенно актуально:

  • в обработке сетевых пакетов,
  • при работе с IoT или микроконтроллерами,
  • при сериализации/десериализации.

📌 Заключение

Битовые сдвиги — это не только олдскульные трюки, но и актуальные оптимизации, особенно в задачах, где:

  • важен каждый байт,
  • данные нужно упаковать,
  • скорость критична.

В Python ими пользуются реже — но это даёт преимущество тем, кто умеет. Вас легко заметят на собеседовании, в код-ревью, и даже на хакатоне.