Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
slots: Оптимизация памяти в Python
Определение
slots - это механизм в Python для явного определения атрибутов класса и экономии памяти путём исключения dict для каждого объекта.
Как работает память без slots
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Иван", 28)
# У каждого объекта есть __dict__ - словарь всех атрибутов
print(person.__dict__) # {'name': 'Иван', 'age': 28}
# Словари занимают много памяти (248+ байт за словарь)
# Если создать 1,000,000 объектов, это ~250 MB только на __dict__
Введение slots
class PersonWithSlots:
__slots__ = ['name', 'age'] # только эти атрибуты разрешены
def __init__(self, name, age):
self.name = name
self.age = age
person = PersonWithSlots("Иван", 28)
# Нет __dict__!
print(hasattr(person, '__dict__')) # False
# Но атрибуты работают:
print(person.name) # Иван
print(person.age) # 28
Сравнение памяти
import sys
class PersonNormal:
def __init__(self, name, age):
self.name = name
self.age = age
class PersonSlots:
__slots__ = ['name', 'age']
def __init__(self, name, age):
self.name = name
self.age = age
# Создай по одному объекту
normal = PersonNormal("Иван", 28)
slots = PersonSlots("Иван", 28)
print(f"Нормальный класс: {sys.getsizeof(normal)} байт")
# Выпуск: 344 байт
print(f"Со __slots__: {sys.getsizeof(slots)} байт")
# Выпуск: 48 байт
print(f"Экономия: {344 - 48} байт (86% меньше!)")
Практический пример
import sys
from memory_profiler import profile
# Без __slots__ (памятезатратно)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# Со __slots__ (экономно)
class PointSlots:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
# Создай 1,000,000 объектов
normal_points = [Point(i, i+1) for i in range(1_000_000)]
slots_points = [PointSlots(i, i+1) for i in range(1_000_000)]
# Проверь память
print(f"Нормальные Point: {sys.getsizeof(normal_points)}")
print(f"Point со __slots__: {sys.getsizeof(slots_points)}")
# Примерные результаты:
# Нормальные: ~320-350 МБ
# __slots__: ~48-80 МБ (в 4-5 раз меньше!)
Какие операции блокируются slots?
class Person:
__slots__ = ['name', 'age']
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Иван", 28)
# Работает
print(person.name) # OK
person.age = 29 # OK
# НЕ работает - динамическое добавление атрибутов
person.email = "ivan@example.com" # AttributeError!
# НЕ работает - нет __dict__
print(person.__dict__) # AttributeError!
# НЕ работает - нельзя удалить из __slots__
del person.name # AttributeError!
Наследование и slots
# Родительский класс
class Animal:
__slots__ = ['name']
# Дочерний класс - нужно добавить свои __slots__
class Dog(Animal):
__slots__ = ['breed'] # добавляем новые
def __init__(self, name, breed):
self.name = name
self.breed = breed
dog = Dog("Шарик", "Овчарка")
print(dog.name) # OK
print(dog.breed) # OK
# Если забыть __slots__ в наследнике
class Cat(Animal):
# Нет __slots__!
def __init__(self, name, color):
self.name = name
self.color = color
cat = Cat("Мурзик", "чёрный")
print(cat.__dict__) # {'color': 'чёрный'} - есть __dict__!
cat.new_attr = 123 # Работает, можно добавить
Методы и свойства
class Person:
__slots__ = ['_name', '_age']
def __init__(self, name, age):
self._name = name
self._age = age
# Свойства (properties) работают со __slots__
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value:
raise ValueError("Имя не может быть пустым")
self._name = value
def birthday(self):
self._age += 1
person = Person("Иван", 28)
print(person.name) # OK
person.name = "Петр" # OK
person.birthday() # OK
Когда использовать slots
Используй slots когда:
-
Много объектов (миллионы экземпляров)
# Пример: приложение обрабатывает миллионы пользователей users = [User(...) for _ in range(10_000_000)] -
Критична память (встроенные системы, серверы)
# IoT устройство с ограниченной памятью # каждый MB важен -
Стабильное множество атрибутов
# Класс Data Point с фиксированными полями class DataPoint: __slots__ = ['timestamp', 'value', 'sensor_id'] -
Хочешь избежать случайных опечаток
# Без __slots__ можно случайно создать новый атрибут person.nme = "Иван" # опечатка, но работает # Со __slots__ ловится ошибка # person.nme = "Иван" # AttributeError!
НЕ используй slots когда:
- Мало объектов (десятки, сотни)
- Нужна гибкость (добавлять новые атрибуты)
- Используешь множественное наследование (может быть проблемы)
- Нужен JSON сериализация через dict
Практический пример в ML
import numpy as np
from typing import List
# Класс для хранения признаков (features) датасета
class Feature:
__slots__ = ['name', 'values', 'dtype']
def __init__(self, name: str, values: np.ndarray):
self.name = name
self.values = values
self.dtype = values.dtype
def normalize(self):
mean = np.mean(self.values)
std = np.std(self.values)
self.values = (self.values - mean) / std
# Создай 1000 признаков для большого датасета
features = [
Feature(f"feature_{i}", np.random.randn(1000))
for i in range(1000)
]
# Со __slots__ это занимает ~2x меньше памяти
print(f"Всего объектов: {len(features)}")
print(f"Каждый объект: {48} байт (со __slots__)")
print(f"Всего: ~{len(features) * 48 / 1024 / 1024:.1f} МБ")
Ограничения slots
1. Нельзя использовать __dict__
person.__dict__ = {...} # Error
2. Нельзя использовать __weakref__ по умолчанию
# Нужно добавить:
__slots__ = ['name', '__weakref__']
3. Сложнее с множественным наследованием
class A:
__slots__ = ['a']
class B:
__slots__ = ['b']
class C(A, B): # Проблема!
pass
4. Pickle и сериализация могут быть сложнее
Отладка
class Person:
__slots__ = ['name', 'age']
person = Person()
person.name = "Иван"
# Посмотреть доступные атрибуты
print(Person.__slots__) # ['name', 'age']
# Проверить, есть ли атрибут
print(hasattr(person, 'name')) # True
print(hasattr(person, 'email')) # False
# Список всех слотов класса
for cls in Person.__mro__: # Method Resolution Order
if hasattr(cls, '__slots__'):
print(f"{cls.__name__}: {cls.__slots__}")
Заключение
slots - это инструмент для оптимизации памяти в Python. Если ты работаешь с большим количеством объектов и память критична, slots может снизить использование памяти в 4-10 раз. Однако это приносит потерю гибкости (нельзя добавлять новые атрибуты). Выбирай slots когда есть реальная потребность в оптимизации памяти, а не профилактически.