← Назад к вопросам
Возможно ли создать список с определенным количеством ячеек памяти?
2.0 Middle🔥 141 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Краткое резюме
Да, в Python возможно создавать список с предопределенным количеством ячеек памяти. Это достигается на нескольких уровнях: (1) выделение капацитета — спис выделяет больше памяти, чем нужно, для быстрых append; (2) предварительное заполнение — создать список нужным числом элементов; (3) использование array и NumPy — для точного контроля. В практика Python список динамически управляет памятью, но можно оптимизировать для производительности.
Вутрннее устройство спска Python
Динамическое выделение памяти
Способ 1: Предварительное заполнение списка
import sys
# Способ 1: Список из None
size = 1000
my_list = [None] * size
print(f"Размер: {sys.getsizeof(my_list)} байт")
print(f"Длина: {len(my_list)}")
# Способ 2: Список с конкретными значениями
my_list = [0] * size
for i in range(size):
my_list[i] = i
# Способ 3: С помощью list comprehension
my_list = [0 for _ in range(size)]
# Способ 4: С помощью range
my_list = list(range(size))
Производительность предварительного выделения
import time
size = 100_000
# Способ 1: append (динамическое расширение)
start = time.monotonic()
list_dynamic = []
for i in range(size):
list_dynamic.append(i)
print(f"append: {(time.monotonic()-start)*1000:.2f}ms")
# Способ 2: Pre-allocated (заранее выделенный список)
start = time.monotonic()
list_preallocated = [0] * size
for i in range(size):
list_preallocated[i] = i
print(f"pre-allocated: {(time.monotonic()-start)*1000:.2f}ms")
# Pre-allocated на ~40% быстрее
Способ 2: Использование array модуля
Для точного контроля памяти используй модуль array:
import array
import sys
# array — это контейнер с фиксированным типом элементов
# Целые числа (4 байта каждое)
int_array = array.array('i', [1, 2, 3, 4, 5])
print(f"array('i'): {sys.getsizeof(int_array)} байт")
int_list = [1, 2, 3, 4, 5]
print(f"list: {sys.getsizeof(int_list)} байт")
# Для большого размера
size = 1_000_000
int_array = array.array('i')
int_list = []
print(f"Для {size:,} чисел:")
print(f"array('i'): {size * 4:,} байт (~4MB)")
print(f"list: {size * 8:,} байт (~8MB)")
Способ 3: NumPy для точного контроля
NumPy — лучший выбор для работы с большими объёмами данных:
import numpy as np
# Создание NumPy массива с точным размером
size = 1_000_000
# Int32 (4 байта на элемент)
int32_array = np.zeros(size, dtype=np.int32)
print(f"np.int32[{size:,}]: {int32_array.nbytes:,} байт")
# Float64 (8 байт на элемент)
float64_array = np.zeros(size, dtype=np.float64)
print(f"np.float64[{size:,}]: {float64_array.nbytes:,} байт")
# Быстрое заполнение значениями
data = np.arange(size, dtype=np.int32)
print(f"Значения: {data[:5]}...")
Сравнение: Память и скорость
import time
import array
import numpy as np
size = 1_000_000
print(f"Для {size:,} элементов:")
# 1. Python list
start = time.monotonic()
py_list = [0] * size
for i in range(size):
py_list[i] = i
py_time = time.monotonic() - start
print(f"list: {py_time*1000:.2f}ms")
# 2. array.array
start = time.monotonic()
array_obj = array.array('i', range(size))
arr_time = time.monotonic() - start
print(f"array.array: {arr_time*1000:.2f}ms")
# 3. NumPy
start = time.monotonic()
np_array = np.arange(size, dtype=np.int32)
np_time = time.monotonic() - start
print(f"numpy: {np_time*1000:.2f}ms")
Практический пример: Буфер с предварительным выделением
import time
class RingBuffer:
"""Круговой буфер с предварительным выделением памяти"""
def __init__(self, size: int):
self.buffer = [None] * size # Выделяем всю память заранее
self.size = size
self.index = 0
def append(self, value):
"""Добавить значение в буфер"""
self.buffer[self.index] = value
self.index = (self.index + 1) % self.size
def get_all(self):
"""Получить все значения в правильном порядке"""
return self.buffer[self.index:] + self.buffer[:self.index]
# Использование
buffer = RingBuffer(1000)
# Добавляем много значений в фиксированный буфер
start = time.monotonic()
for i in range(1_000_000):
buffer.append(i)
print(f"1M append в RingBuffer: {(time.monotonic()-start)*1000:.2f}ms")
Лучшие практики
1. Когда использовать pre-allocation
# Используй pre-allocation когда:
# - Знаешь размер заранее
# - Добавляешь много элементов
# - Производительность критична
size = 100_000
# ПЛОХО
result = []
for i in range(size):
result.append(calculate(i))
# ХОРОШО
result = [0] * size
for i in range(size):
result[i] = calculate(i)
2. Выбор структуры данных
import array
import numpy as np
# Маленькие числовые данные
data = array.array('i', [1, 2, 3])
# Большие числовые данные
data = np.array([1, 2, 3], dtype=np.int32)
# Смешанные типы
data = [1, "string", 3.14]
Заключение
- Python динамически управляет памятью, но ты можешь её предварительно выделить
- Pre-allocation работает быстрее на 30-40% для known-size списков
- array.array экономит память в 2x для примитивных типов
- NumPy лучше для больших данных
- Выбор зависит от задачи: для обычного кода используй список, для performance-critical используй array или NumPy