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

Kak Optimizirovat Skripty Python Dla Povysenia Proizvoditel Nosti



Оптимизация сценариев Python для повышения производительности включает в себя выявление и устранение узких мест в нашем коде, что позволяет ему работать быстрее и эффективнее. Python — популярный и мощный язык программирования, который в настоящее время используется во многих приложениях, включая анализ данных, проекты ML (машинное обучение), веб-разработку и многое другое. Оптимизация кода Python — это стратегия повышения скорости и эффективности программы разработчика при выполнении любых действий с использованием меньшего количества строк кода, меньшего количества памяти или дополнительных ресурсов. Большой и неэффективный код может замедлить работу программы, что может привести к снижению удовлетворенности клиентов и потенциальным финансовым потерям или к необходимости дополнительной работы по исправлению и устранению неполадок.

Это необходимо при выполнении задачи, требующей обработки нескольких действий или данных. Таким образом, отключение и улучшение некоторых неэффективных блоков кода и функций может дать потрясающие результаты, подобные следующим:

  1. Повысьте производительность приложения
  2. Создавайте читаемый и организованный код
  3. Упростите мониторинг ошибок и отладку
  4. Сохранение значительной вычислительной мощности и т. д.

Профилируйте свой код

Прежде чем приступить к оптимизации, важно определить части кода проекта, которые замедляют его работу. Методы профилирования в Python включают пакеты cProfile и Profile. Используйте такие инструменты, чтобы оценить, насколько быстро выполняются определенные функции и строки кода. Модуль cProfile создает отчет, в котором подробно указано, сколько времени требуется для выполнения каждой функции сценария. Этот отчет может помочь нам найти функции, которые работают медленно, и улучшить их.







Фрагмент кода:



Импортировать cПрофиль как КП
защита вычислить сумму ( входной номер ) :
sum_of_input_numbers '=' 0
пока входной номер > 0 :
sum_of_input_numbers + '=' входной номер % 10
входной номер // '=' 10
Распечатать ( «Сумма всех цифр во входном номере: 'sum_of_input_numbers'» )
возвращаться sum_of_input_numbers
защита main_func ( ) :
КП. бегать ( 'вычислитьСумму(9876543789)' )
если __имя__ == '__основной__' :
main_func ( )

Программа выполняет в общей сложности пять вызовов функций, как видно из первой строки вывода. Подробности каждого вызова функции показаны в следующих нескольких строках, включая количество вызовов функции, общую продолжительность времени в функции, продолжительность времени на вызов и общее количество времени в функции (включая все функции, которые он вызывает).



Кроме того, программа печатает отчет на экране подсказки, который показывает, что программа завершает выполнение всех своих задач в течение 0,000 секунд. Это показывает, насколько быстрая программа.





Выберите правильную структуру данных

Характеристики производительности зависят от структуры данных. В частности, словари обеспечивают более быстрый поиск, чем списки, касающиеся хранилища общего назначения. Выберите структуру данных, наиболее подходящую для операций, которые мы будем проводить с вашими данными, если вы их знаете. В следующем примере исследуется эффективность различных структур данных для идентичного процесса, чтобы определить, присутствует ли элемент в структуре данных.



Мы оцениваем время, необходимое для проверки наличия элемента в каждой структуре данных — списке, наборе и словаре — и сравниваем их.

Оптимизедататипе.py:

Импортировать Тимей как тт
Импортировать случайный как рндобж
# Генерируем список целых чисел
случайный_список_данных '=' [ rndobj. случайный ( 1 , 10000 ) для _ в диапазон ( 10000 ) ]
# Создаем набор из тех же данных
случайный_набор_данных '=' набор ( случайный_список_данных )

# Создаем словарь с теми же данными, что и ключи
obj_DataDictionary '=' { на одной: Никто для на одной в случайный_список_данных }

# Элемент для поиска (существует в данных)
случайное_число_для_нахождения '=' rndobj. выбор ( случайный_список_данных )

# Измерьте время, чтобы проверить членство в списке
list_time '=' тт. Тимей ( лямбда : случайное_число_для_нахождения в случайный_список_данных , число '=' 1000 )

# Измерьте время, чтобы проверить членство в наборе
установить время '=' тт. Тимей ( лямбда : случайное_число_для_нахождения в случайный_набор_данных , число '=' 1000 )

# Измерьте время, чтобы проверить членство в словаре
dict_time '=' тт. Тимей ( лямбда : случайное_число_для_нахождения в obj_DataDictionary , число '=' 1000 )

Распечатать ( ж «Время проверки членства в списке: {list_time:.6f} секунд» )
Распечатать ( ж «Установить время проверки членства: {set_time:.6f} секунд» )
Распечатать ( ж «Время проверки членства в словаре: {dict_time:.6f} секунд» )

Этот код сравнивает производительность списков, наборов и словарей при проверке членства. В общем, наборы и словари значительно быстрее, чем списки для тестов на членство, поскольку они используют поиск на основе хеша, поэтому их средняя временная сложность равна O(1). С другой стороны, списки должны выполнять линейный поиск, что приводит к проверке принадлежности с временной сложностью O(n).

  Снимок экрана компьютера. Описание создается автоматически.

Используйте встроенные функции вместо циклов

Многочисленные встроенные функции и методы Python можно использовать для выполнения типичных задач, таких как фильтрация, сортировка и сопоставление. Использование этих подпрограмм вместо создания собственных циклов помогает ускорить код, поскольку они часто оптимизированы по производительности.

Давайте создадим пример кода, чтобы сравнить производительность создания пользовательских циклов, используя встроенные функции для типичных заданий (таких как map(), filter() и sorted()). Мы оценим, насколько хорошо работают различные методы отображения, фильтрации и сортировки.

ВстроенныйФункция.py:

Импортировать Тимей как тт
# Пример списка номеров_list
Numbers_list '=' список ( диапазон ( 1 , 10000 ) )

# Функция возведения в квадрат чисел_списка с использованием цикла
защита Square_using_loop ( Numbers_list ) :
Square_result '=' [ ]
для на одной в номера_список:
Square_result. добавить ( на одной ** 2 )
возвращаться Square_result
# Функция для фильтрации четных чисел_списка с использованием цикла
защита filter_even_using_loop ( Numbers_list ) :
filter_result '=' [ ]
для на одной в номера_список:
если на одной % 2 == 0 :
filter_result. добавить ( на одной )
возвращаться filter_result
# Функция для сортировки Number_list с помощью цикла
защита sort_using_loop ( Numbers_list ) :
возвращаться отсортированный ( Numbers_list )
# Измерьте время возведения в квадрат чисел_списка с помощью карты()
карта_время '=' тт. Тимей ( лямбда : список ( карта ( лямбда х: х** 2 , Numbers_list ) ) , число '=' 1000 )
# Измерьте время для фильтрации четных чисел_списка с помощью фильтра()
filter_time '=' тт. Тимей ( лямбда : список ( фильтр ( лямбда х: х % 2 == 0 , Numbers_list ) ) , число '=' 1000 )
# Измерьте время сортировки Number_list с помощью sorted()
сортированное_время '=' тт. Тимей ( лямбда : отсортированный ( Numbers_list ) , число '=' 1000 )
# Измеряем время возведения в квадрат чисел_списка с помощью цикла
цикл_карта_время '=' тт. Тимей ( лямбда : Square_using_loop ( Numbers_list ) , число '=' 1000 )
# Измерьте время для фильтрации четных чисел_списка с помощью цикла
цикл_фильтр_время '=' тт. Тимей ( лямбда : filter_even_using_loop ( Numbers_list ) , число '=' 1000 )
# Измеряем время сортировки Number_list с помощью цикла
циклическое_сортированное_время '=' тт. Тимей ( лямбда : sort_using_loop ( Numbers_list ) , число '=' 1000 )
Распечатать ( «Список номеров содержит 10000 элементов» )
Распечатать ( ж «Время Map(): {map_time:.6f} секунд» )
Распечатать ( ж «Время Filter(): {filter_time:.6f} секунд» )
Распечатать ( ж «Время Sorted(): {sorted_time:.6f} секунд» )
Распечатать ( ж «Время цикла (карты): {loop_map_time:.6f} секунд» )
Распечатать ( ж «Время цикла (фильтра): {loop_filter_time:.6f} секунд» )
Распечатать ( ж «Время цикла (сортировка): {loop_sorted_time:.6f} секунд» )

Вероятно, мы заметим, что встроенные функции (map(), filter() и sorted()) работают быстрее, чем пользовательские циклы для решения этих распространенных задач. Встроенные функции Python предлагают более краткий и понятный подход к выполнению этих задач и высоко оптимизированы по производительности.

Оптимизируйте циклы

Если написание циклов необходимо, есть несколько методов, которые мы можем использовать, чтобы ускорить их. Как правило, цикл range() выполняется быстрее, чем итерация назад. Это связано с тем, что range() генерирует итератор без инвертирования списка, что может оказаться дорогостоящей операцией для длинных списков. Кроме того, поскольку функция range() не создает новый список в памяти, она использует меньше памяти.

ОптимизироватьLoop.py:

Импортировать Тимей как тт
# Пример списка номеров_list
Numbers_list '=' список ( диапазон ( 1 , 100000 ) )
# Функция для перебора списка в обратном порядке
защита цикл_обратная_итерация ( ) :
result_reverse '=' [ ]
для дж в диапазон ( только ( Numbers_list ) - 1 , - 1 , - 1 ) :
result_reverse. добавить ( Numbers_list [ дж ] )
возвращаться result_reverse
# Функция для перебора списка с помощью range()
защита цикл_диапазон_итерация ( ) :
диапазон_результата '=' [ ]
для к в диапазон ( только ( Numbers_list ) ) :
диапазон_результата. добавить ( Numbers_list [ к ] )
возвращаться диапазон_результата
# Измерьте время, необходимое для выполнения обратной итерации
обратное_время '=' тт. Тимей ( цикл_обратная_итерация , число '=' 1000 )
# Измерьте время, необходимое для выполнения итерации диапазона
диапазон_время '=' тт. Тимей ( цикл_диапазон_итерация , число '=' 1000 )
Распечатать ( «Список номеров содержит 100 000 записей» )
Распечатать ( ж «Время обратной итерации: {reverse_time:.6f} секунд» )
Распечатать ( ж «Время итерации диапазона: {range_time:.6f} секунд» )

Избегайте ненужных вызовов функций

Каждый раз при вызове функции возникают некоторые накладные расходы. Код выполняется быстрее, если избегать ненужных вызовов функций. Например, вместо многократного выполнения функции, вычисляющей значение, попробуйте сохранить результат вычисления в переменной и использовать его.

Инструменты для профилирования

Чтобы узнать больше о производительности вашего кода, помимо встроенного профилирования, мы можем использовать внешние пакеты профилирования, такие как cProfile, Pyflame или SnakeViz.

Кэшировать результаты

Если нашему коду необходимо выполнить дорогостоящие вычисления, мы могли бы рассмотреть возможность кэширования результатов, чтобы сэкономить время.

Рефакторинг кода

Рефакторинг кода, чтобы его было легче читать и поддерживать, иногда является необходимой частью его оптимизации. Более быстрая программа также может быть чище.

Используйте JIT-компиляцию.

Такие библиотеки, как PyPy или Numba, могут обеспечить JIT-компиляцию, которая может значительно ускорить некоторые типы кода Python.

Обновить Python

Убедитесь, что вы используете последнюю версию Python, поскольку новые версии часто включают улучшения производительности.

Параллелизм и параллелизм

Для процессов, которые можно распараллелить, изучите методы параллельного выполнения и синхронизации, такие как многопроцессорность, многопоточность или асинхронность.

Помните, что бенчмаркинг и профилирование должны быть основными факторами оптимизации. Сосредоточьтесь на улучшении тех областей нашего кода, которые оказывают наиболее существенное влияние на производительность, и постоянно тестируйте свои улучшения, чтобы убедиться, что они дают желаемый эффект, не внося новых дефектов.

Заключение

В заключение, оптимизация кода Python имеет решающее значение для повышения производительности и эффективности использования ресурсов. Разработчики могут значительно повысить скорость выполнения и скорость реагирования своих приложений Python, используя различные методы, такие как выбор соответствующих структур данных, использование встроенных функций, сокращение дополнительных циклов и эффективное управление памятью. Постоянное тестирование производительности и профилирование должны направлять усилия по оптимизации, гарантируя, что усовершенствования кода соответствуют реальным требованиям к производительности. Чтобы гарантировать долгосрочный успех проекта и снизить вероятность появления новых проблем, оптимизация кода должна постоянно сочетаться с целями читаемости и удобства сопровождения кода.