← Назад к вопросам
Что больше занимает памяти 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)) # Небольшой размер
Вывод
- Range занимает ~48 байт независимо от размера
- Список занимает память пропорционально количеству элементов
- Range вычисляет значения, а не хранит их
- Используй range в циклах — это профессионально и эффективно
- Преобразуй в list только если действительно нужен доступ к элементам вне порядка