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

Для чего нужен slots?

2.2 Middle🔥 61 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Для чего нужен slots?

__slots__ — это специальный атрибут в Python классах, который определяет, какие атрибуты экземпляра класса могут существовать. Это инструмент для оптимизации памяти и производительности.

Как работает slots

По умолчанию Python хранит атрибуты экземпляра в словаре (__dict__), что требует дополнительной памяти. С __slots__ можно указать точный список допустимых атрибутов.

# Без __slots__: использует __dict__
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

user = User("John", "john@example.com")
print(user.__dict__)  # {'name': 'John', 'email': 'john@example.com'}
print(sys.getsizeof(user.__dict__))  # Словарь требует памяти

# С __slots__: фиксированный список атрибутов
class OptimizedUser:
    __slots__ = ['name', 'email']
    
    def __init__(self, name, email):
        self.name = name
        self.email = email

user = OptimizedUser("John", "john@example.com")
# Нет __dict__, атрибуты хранятся более эффективно

Основные преимущества

1. Экономия памяти

import sys

# Класс без __slots__
class RegularUser:
    def __init__(self, name, email, age):
        self.name = name
        self.email = email
        self.age = age

# Класс со __slots__
class SlotUser:
    __slots__ = ['name', 'email', 'age']
    
    def __init__(self, name, email, age):
        self.name = name
        self.email = email
        self.age = age

regular = RegularUser("John", "john@example.com", 30)
slot = SlotUser("John", "john@example.com", 30)

print(f"Regular size: {sys.getsizeof(regular)} bytes")
print(f"Slot size: {sys.getsizeof(slot)} bytes")
print(f"Regular dict size: {sys.getsizeof(regular.__dict__)} bytes")

# Примерный результат:
# Regular size: 56 bytes
# Regular dict size: 240 bytes (словарь требует много памяти)
# Slot size: 56 bytes
# Слоты экономят память словаря!

На большом количестве объектов экономия значительна:

# Создаём 100 000 пользователей
import sys

regular_users = [RegularUser(f"user{i}", f"user{i}@example.com", i) for i in range(100000)]
slot_users = [SlotUser(f"user{i}", f"user{i}@example.com", i) for i in range(100000)]

regular_memory = sum(sys.getsizeof(user.__dict__) for user in regular_users)
slot_memory = 0  # Слоты не используют отдельные словари

print(f"Regular memory: {regular_memory / 1024 / 1024:.2f} MB")
print(f"Slot memory: {slot_memory / 1024 / 1024:.2f} MB")
# Экономия может быть в несколько десятков мегабайт

2. Безопасность и контроль

# Со __slots__: не можешь добавлять новые атрибуты
class StrictUser:
    __slots__ = ['name', 'email']
    
    def __init__(self, name, email):
        self.name = name
        self.email = email

user = StrictUser("John", "john@example.com")
user.age = 30  # AttributeError: 'StrictUser' object has no attribute 'age'

# Это может быть полезно для предотвращения случайных ошибок
# или создания API с контролируемым интерфейсом

3. Производительность

import timeit

class NormalClass:
    def __init__(self, x):
        self.x = x

class SlotClass:
    __slots__ = ['x']
    
    def __init__(self, x):
        self.x = x

# Доступ к атрибутам со __slots__ немного быстрее
normal = NormalClass(10)
slot = SlotClass(10)

normal_time = timeit.timeit(lambda: normal.x, number=1000000)
slot_time = timeit.timeit(lambda: slot.x, number=1000000)

print(f"Normal: {normal_time:.4f}s")
print(f"Slot: {slot_time:.4f}s")
print(f"Быстрее на: {(normal_time - slot_time) / normal_time * 100:.2f}%")

Когда использовать slots

1. Большое количество объектов

# Если создаёшь миллионы объектов, __slots__ экономит память
class DataPoint:
    __slots__ = ['x', 'y', 'z', 'timestamp']
    
    def __init__(self, x, y, z, timestamp):
        self.x = x
        self.y = y
        self.z = z
        self.timestamp = timestamp

# 1 млн точек данных
data = [DataPoint(i, i*2, i*3, time.time()) for i in range(1000000)]

2. Фиксированный набор атрибутов

# Модель с известным набором полей
class Product:
    __slots__ = ['id', 'name', 'price', 'description']
    
    def __init__(self, id, name, price, description):
        self.id = id
        self.name = name
        self.price = price
        self.description = description

3. Performance-критичные операции

# Когда требуется максимальная производительность
class Vector:
    __slots__ = ['x', 'y', 'z']
    
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z
    
    def __add__(self, other):
        return Vector(
            self.x + other.x,
            self.y + other.y,
            self.z + other.z
        )

Недостатки slots

1. Снижает гибкость

class User:
    __slots__ = ['name', 'email']

user = User()
user.age = 30  # AttributeError! Не можешь добавлять новые атрибуты

# Это может быть проблемой если нужна динамичность

2. Усложняет наследование

# Базовый класс со __slots__
class Animal:
    __slots__ = ['name']

# Подкласс без __slots__: получит __dict__
class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__()
        self.name = name
        self.breed = breed  # Работает, но требует __dict__

# Правильное наследование
class Cat(Animal):
    __slots__ = ['color']  # Дополнительные slots для подкласса
    
    def __init__(self, name, color):
        super().__init__()
        self.name = name
        self.color = color

3. Не работает с множественным наследованием (в большинстве случаев)

# Проблема: множественное наследование со __slots__
class Base1:
    __slots__ = ['a']

class Base2:
    __slots__ = ['b']

class Child(Base1, Base2):  # Может быть сложно
    __slots__ = ['c']

4. Нельзя использовать методы типа setattr без __dict__

class Config:
    __slots__ = ['debug', 'timeout']

config = Config()
setattr(config, 'debug', True)  # Работает
setattr(config, 'new_attr', 123)  # AttributeError

Примеры использования в реальном коде

Django модель (если не используешь ORM)

class UserModel:
    __slots__ = ['id', 'username', 'email', 'created_at']
    
    def __init__(self, id, username, email, created_at):
        self.id = id
        self.username = username
        self.email = email
        self.created_at = created_at

Dataclass со slots

from dataclasses import dataclass

@dataclass
class Point:
    __slots__ = ['x', 'y']
    x: float
    y: float

Pydantic модель (Pydantic v2 поддерживает slots)

from pydantic import BaseModel

class User(BaseModel):
    model_config = {"slots": True}  # Включить __slots__
    
    id: int
    name: str
    email: str

Заключение

__slots__ — это мощный инструмент для оптимизации памяти и производительности при работе с большим количеством объектов. Используй его когда:

  • Создаёшь много объектов (тысячи+)
  • Фиксированный набор атрибутов известен заранее
  • Критична экономия памяти
  • Требуется высокая производительность

Не используй когда:

  • Нужна гибкость добавления новых атрибутов
  • Работаешь с небольшим количеством объектов
  • Используешь множественное наследование
  • Нужна динамичность
Для чего нужен slots? | PrepBro