Сообщить об ошибке.

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

Содержание:

Использование локальных переменных в методах действительно может ускорить код. В материале разберем когда и почему это работает.

Почему локальные переменные быстрее?

  1. Локальные переменные находятся в locals() - быстрый доступ
  2. Атрибуты объекта требуют поиска через self.__dict__ - медленнее
  3. Каждый доступ через self - это вызов __getattribute__

Разница в доступе к переменным:

# Медленнее - доступ через self
class SlowProcessor:
    def __init__(self, data):
        self.data = data
        self.multiplier = 2
    
    def process(self):
        total = 0
        # ДОСТУП через self - медленнее
        for item in self.data:
            # ДОСТУП через self - медленнее
            total += item * self.multiplier  
        return total

# Быстрее - локальные переменные
class FastProcessor:
    def __init__(self, data):
        self.data = data
        self.multiplier = 2
    
    def process(self):
        # КЭШИРУЕМ в локальные переменные
        data = self.data
        # КЭШИРУЕМ в локальные переменные
        multiplier = self.multiplier     
        total = 0
        # БЫСТРЫЙ доступ к локальной переменной
        for item in data:
            # БЫСТРЫЙ доступ к локальной переменной
            total += item * multiplier
        return total

Тест производительности

import time

# Тест производительности
def performance_test():
    data = list(range(1000000))
    
    # Медленная версия
    class SlowProcessor:
        def __init__(self, data):
            self.data = data
        
        def sum_squares(self):
            total = 0
            for item in self.data:  # Медленный доступ
                total += item * item
            return total
    
    # Быстрая версия
    class FastProcessor:
        def __init__(self, data):
            self.data = data
        
        def sum_squares(self):
            data = self.data  # Кэшируем в локальную переменную
            total = 0
            for item in data:  # Быстрый доступ
                total += item * item
            return total
    
    # Тест медленной версии
    slow = SlowProcessor(data)
    start = time.time()
    result1 = slow.sum_squares()
    slow_time = time.time() - start
    
    # Тест быстрой версии
    fast = FastProcessor(data)
    start = time.time()
    result2 = fast.sum_squares()
    fast_time = time.time() - start
    
    print(f"Медленная версия: {slow_time:.4f} сек")
    print(f"Быстрая версия: {fast_time:.4f} сек")
    print(f"Ускорение: {slow_time/fast_time:.2f}x")
    print(f"Результаты равны: {result1 == result2}")

performance_test()
# Медленная версия: 0.0692 сек
# Быстрая версия: 0.0604 сек
# Ускорение: 1.12x
# Результаты равны: True

Когда стоит применять оптимизацию?

Нужна оптимизацию когда:

  • Класс обрабатывает большие объемы данных
  • Метод вызывается часто (в циклах, при каждом запросе)
  • Профилирование показало узкое место в производительности

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

  • Метод вызывается редко
  • Код становится нечитаемым
  • Выигрыш меньше 2% производительности

Практическое задание

Ответь на вопросы:

  1. Почему локальные переменные быстрее атрибутов объекта?
  2. Когда не стоит применять эту оптимизацию?
  3. Как бы оптимизировал метод get_top_rated_books в Rating?
  4. Можно ли использовать эту технику в веб-приложениях?
Ответы
  1. Потому что локальные переменные находятся в быстром словаре locals(), а атрибуты требуют поиска через __getattribute__
  2. Когда код становится менее читаемым, когда выигрыш минимальный, когда методы вызываются редко
  3. Кэшировать ссылки на методы базы данных и использовать локальные переменные для циклов
  4. Да, особенно в высоконагруженных частях (обработка запросов, генерация отчетов)