← Назад к вопросам

Что больше занимает памяти range или список в Python?

2.0 Middle🔥 71 комментариев
#DevOps и инфраструктура#REST API и HTTP#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Memory usage: range vs список в Python

Range занимает несоизмеримо МЕНЬШЕ памяти чем список. Это одна из самых элегантных оптимизаций в Python.

Почему range экономит память

Range — это ленивый генератор значений:

  • Не хранит все числа в памяти
  • Вычисляет каждое число по формуле start + step * i
  • Нужна всего одна небольшая структура данных

Список — это материализованный массив:

  • Хранит все элементы в памяти
  • Каждый элемент — отдельный объект Python
  • Память растёт линейно с количеством элементов

Практическое сравнение

import sys

# Range: почти константная память
range_obj = range(1_000_000_000)  # Миллиард элементов!
print(f"Размер range: {sys.getsizeof(range_obj)} байт")
# Вывод: Размер range: 48 байт

# Список: линейная память
list_obj = list(range(1_000_000_000))  # Это зависнет!
# Это заняло бы примерно 30+ ГБ памяти

# Реалистичный пример
small_list = list(range(1_000_000))  # Миллион элементов
print(f"Размер списка на 1М элементов: {sys.getsizeof(small_list) / 1024 / 1024:.1f} МБ")
# Вывод: Размер списка на 1М элементов: ~28.6 МБ

small_range = range(1_000_000)
print(f"Размер range на 1М элементов: {sys.getsizeof(small_range)} байт")
# Вывод: Размер range на 1М элементов: 48 байт

Почему это работает

Range в CPython хранит только три целых числа:

class RangeObject:
    def __init__(self, start, stop, step):
        self.start = start  # Начало
        self.stop = stop    # Конец
        self.step = step    # Шаг
    
    def __getitem__(self, index):
        # Вычисляет значение по формуле
        return self.start + index * self.step

# То есть:
r = range(0, 1_000_000_000, 1)
# Памяти нужна для хранения: start=0, stop=1B, step=1
# Не для 1 миллиарда целых чисел!

Список хранит все элементы:

small_list = [0, 1, 2, 3, 4, 5]  # Каждое число — объект в памяти

import sys
print(sys.getsizeof([]))  # Пустой список: 56 байт
print(sys.getsizeof([0]))  # Один элемент: 64 байта
print(sys.getsizeof([0, 1]))  # Два элемента: 72 байта

# Память растёт с каждым элементом

Детальное объяснение через sys.getsizeof

import sys

def memory_demo():
    # Range — константа независимо от размера
    r1 = range(10)
    r2 = range(1_000_000)
    r3 = range(1_000_000_000)
    
    print(f"range(10): {sys.getsizeof(r1)} байт")
    print(f"range(1M): {sys.getsizeof(r2)} байт")
    print(f"range(1B): {sys.getsizeof(r3)} байт")
    # Все выведут 48 байт!
    
    # Список — растёт с каждым элементом
    l1 = list(range(10))
    l2 = list(range(100))
    l3 = list(range(1000))
    
    print(f"\nlist 10 элементов: {sys.getsizeof(l1)} байт")
    print(f"list 100 элементов: {sys.getsizeof(l2)} байт")
    print(f"list 1000 элементов: {sys.getsizeof(l3)} байт")
    # 136, 536, 5016 байт соответственно

memory_demo()

Когда это критично

Плохо — создание большого списка:

# Займёт ГБ памяти
big_list = list(range(100_000_000))

for i in big_list:  # Медленно
    process(i)

Хорошо — использование range:

# Займёт 48 байт
for i in range(100_000_000):  # Быстро
    process(i)

Итерация: одинаково быстро

import timeit

# Итерация по range
time_range = timeit.timeit(
    'for i in range(10_000_000): pass',
    number=1
)

# Итерация по списку
time_list = timeit.timeit(
    'for i in list(range(10_000_000)): pass',
    number=1
)

print(f"range: {time_range:.2f}s")
print(f"list: {time_list:.2f}s")
# list будет МЕДЛЕННЕЕ из-за создания списка

Python 2 vs Python 3

Python 2 (историческое примечание):

# Python 2
range(10)  # Создавал список: [0,1,2,...,9]
xrange(10)  # Ленивый генератор (как range в Python 3)

Python 3 (сейчас):

# Python 3
range(10)  # Ленивый генератор
# Нет xrange, потому что range уже оптимален

Важные нюансы

Range не создаёт объекты

r = range(3)
print(r[0])  # 0 — целое число
print(r[1])  # 1
print(r[2])  # 2
# Каждый раз вычисляется, не берётся из памяти

Можно проверить размер

import sys

print(sys.getsizeof(range(10)))  # 48
print(sys.getsizeof(range(10**9)))  # 48
print(sys.getsizeof(list(range(10))))  # 136
print(sys.getsizeof(list(range(10**6))))  # 8 МБ+

Range поддерживает операции

r = range(0, 100, 2)  # 0, 2, 4, ..., 98

# Проверка принадлежности — O(1)
print(50 in r)  # True (вычисляется по формуле)
print(51 in r)  # False

# Индексирование — O(1)
print(r[10])  # 20
print(r[-1])  # 98

# Срезы — новый range
print(r[5:10])  # range(10, 30, 2)

Практический совет

# ❌ Плохо — создаёшь список без нужды
for i in list(range(1_000_000)):
    do_something(i)

# ✅ Хорошо — используешь range напрямую
for i in range(1_000_000):
    do_something(i)

# ✅ Хорошо — если нужен список (редко)
items = list(range(100))  # Небольшой размер

Вывод

  1. Range занимает ~48 байт независимо от размера
  2. Список занимает память пропорционально количеству элементов
  3. Range вычисляет значения, а не хранит их
  4. Используй range в циклах — это профессионально и эффективно
  5. Преобразуй в list только если действительно нужен доступ к элементам вне порядка
Что больше занимает памяти range или список в Python? | PrepBro