Оптимизация памяти — критически важный навык для работы с большими объемами данных или в условиях ограниченных ресурсов. Рассмотрим основные методы снижения потребления памяти объектами.
__slots__
позволяет явно объявлять атрибуты класса, что исключает создание динамического __dict__
для каждого экземпляра, экономя память.
class RegularUser:
def __init__(self, user_id, name):
self.user_id = user_id
self.name = name
class OptimizedUser:
__slots__ = ['user_id', 'name']
def __init__(self, user_id, name):
self.user_id = user_id
self.name = name
# Сравнение потребления памяти
import sys
print(sys.getsizeof(RegularUser(1, "Alice"))) # ```56-64 байт
print(sys.getsizeof(OptimizedUser(1, "Alice"))) # ```32-40 байт
Экономия: До 40-50% памяти на объект при большом количестве экземпляров.
Для неизменяемых данных эффективна комбинация NamedTuple
с аннотациями типов:
from typing import NamedTuple
class User(NamedTuple):
user_id: int
name: str
email: str = None # Опциональное поле
user = User(1, "Alice")
print(sys.getsizeof(user)) # ```48 байт (меньше обычного класса)
Выбирайте минимально достаточный числовой тип:
import numpy as np
# Вместо стандартных int (28 байт)
small_numbers = np.arange(100, dtype=np.int8) # 1 байт/число
medium_numbers = np.arange(100, dtype=np.int16) # 2 байта/число
Для обработки больших данных заменяйте списки генераторами:
# Плохая практика (хранит все в памяти)
def get_squares(n):
return [x**2 for x in range(n)] # Список
# Хорошая практика
def get_squares_gen(n):
yield from (x**2 for x in range(n)) # Генератор
Методы оптимизации строковых данных:
# 1. Интернирование строк (кеширование)
a = sys.intern('very_long_string_repeated_many_times')
# 2. Использование bytes вместо str для ASCII
data = b'ascii_text' # На 40% меньше памяти
# 3. Хранение в массиве
from array import array
text_array = array('u', 'text') # Массив символов
Выбор правильной структуры данных:
# 1. Массивы вместо списков для примитивов
from array import array
int_array = array('i', [1, 2, 3]) # Экономит ```60% памяти
# 2. Использование frozenset для неизменяемых множеств
constants = frozenset(['MAX', 'MIN', 'DEFAULT'])
# 3. Специализированные коллекции
from collections import deque
queue = deque(maxlen=100) # Фиксированный размер
Инструменты для анализа использования памяти:
# 1. Стандартные средства
import tracemalloc
tracemalloc.start()
# ... ваш код ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
print(stat)
# 2. Сторонние библиотеки
# pip install memory_profiler
from memory_profiler import profile
@profile
def memory_intensive_func():
# ... ваш код ...
Разделение общих данных между объектами:
class CarModel:
_models = {} # Общий кеш
def __new__(cls, model_name):
if model_name not in cls._models:
cls._models[model_name] = super().__new__(cls)
# Инициализация модели
return cls._models[model_name]
class Car:
def __init__(self, model_name, color):
self.model = CarModel(model_name) # Общие данные
self.color = color # Уникальные данные
__slots__
Оптимизация памяти часто требует баланса между производительностью, читаемостью кода и реальными потребностями приложения.