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

Возможно ли создать список с определенным количеством ячеек памяти?

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