Рубрики
Без рубрики

Методы Python для эффективного кода: Производительность, память и удобство использования

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

Автор оригинала: Satwik Kansal.

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

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

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

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

Практика 1: Старайтесь Не Сдувать Память!

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

В отличие от C/C++, интерпретатор Python выполняет управление памятью, и пользователи не имеют никакого контроля над ним. Управление памятью в Python включает в себя частную кучу, которая содержит все объекты Python и структуры данных.

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

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

  • Используйте генераторы для вычисления больших наборов результатов:

Генераторы дают вам ленивую оценку. Вы используете их, повторяя их: либо явно с “четырьмя”, либо неявно, передавая его любой функции или конструкции, которая выполняет итерацию.

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

  • Для больших чисел/хруста данных вы можете использовать библиотеки типа Numpy , которые изящно управляют управлением памятью.

  • Не используйте + для генерации длинных строк — В Python str неизменен, поэтому левая и правая строки должны быть скопированы в новую строку для каждой пары конкатенаций. Если вы объедините четыре строки длиной 10, вы будете копировать (10+10) + ((10+10)+10) + символы вместо всего лишь 40 символов. Все становится квадратично хуже по мере увеличения числа и размера строки. Java оптимизирует этот случай , преобразуя серию конкатенаций для использования StringBuilder некоторое время, но CPython этого не делает. Поэтому рекомендуется использовать синтаксис .format или % (однако они немного медленнее, чем + для коротких строк). Или лучше, если у вас уже есть содержимое, доступное в виде итерируемого объекта, то используйте ".join(iterable_object) , что намного быстрее. Если вы не можете выбрать между .format и % , проверьте этот интересный поток StackOverflow.

    Выход:

  • Используйте slots при определении класса Python. Вы можете сказать Python не использовать динамический dict и выделять пространство только для фиксированного набора атрибутов, устраняя накладные расходы на использование одного dict для каждого объекта, установив __slots__ в классе фиксированный список имен атрибутов. Слоты также предотвращают произвольное присвоение атрибутов объекту, поэтому форма объекта остается неизменной во всем. Подробнее о слотах | читайте здесь . Вы можете

  • отслеживать использование памяти на уровне объекта с помощью встроенных модулей, таких как resource и objgraph . Управление утечками памяти

  • в Python может быть трудной задачей, но, к счастью, есть такие инструменты, как heapy для отладки утечек памяти. Heapy может использоваться вместе с objgraph для наблюдения за ростом распределения объектов diff с течением времени. Heapy может показать, какие объекты содержат больше всего памяти. Objgraph может помочь вам найти обратные ссылки, чтобы точно понять, почему они не могут быть освобождены. Вы можете прочитать больше о диагностике утечек памяти в Python здесь .

Вы можете подробно прочитать об управлении памятью Python разработчиками Theano здесь .

Практика 2: Напишите красивый код, потому что – “Первое впечатление-это последнее впечатление.”

Совместное использование кода-это полезное дело. Какова бы ни была мотивация, ваши благие намерения могут не иметь желаемого результата, если люди сочтут ваш код трудным для использования или понимания. Почти каждая организация следует рекомендациям по стилю, которым разработчики должны следовать для обеспечения согласованности, легкой отладки и простоты совместной работы. Zen of Python – это как мини-руководство по стилю и дизайну для Python. Популярные рекомендации по стилю для Python включают в себя:

  1. Руководство по стилю PEP-8
  2. Python Идиомы и эффективность
  3. Руководство по стилю Google Python

В этих рекомендациях обсуждается, как использовать: пробелы, запятые и фигурные скобки, рекомендации по именованию объектов и т. Д. Хотя в некоторых ситуациях они могут конфликтовать, все они имеют одну и ту же цель — “Чистые, читаемые и отлаживаемые стандарты для кода.”

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

Использование инструментов статического анализа кода

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

Pylint – это инструмент Python, который проверяет модуль на наличие стандартов кодирования. Pylint может быть быстрым и простым способом увидеть, уловил ли ваш код суть PEP-8 и, следовательно, “дружелюбен” к другим потенциальным пользователям.

Он также предоставляет вам отчеты с глубокими метриками и статистикой, которые могут помочь вам оценить качество кода. Вы также можете настроить его, создав свой собственный файл .pylintrc и используя его.

Pylint — не единственный вариант-есть и другие инструменты, такие как PyChecker , PyFlakes , а также пакеты типа pep8 и flakes8 . Моя рекомендация состояла бы в том , чтобы использовать coala , унифицированную структуру статического анализа кода, которая направлена на обеспечение языкового агностического анализа кода с помощью единой структуры. Coala поддерживает все инструменты линтинга, о которых я упоминал ранее, и легко настраивается.

Правильное документирование кода

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

  • Краткое изложение в одну строку того, что делает эта функция.
  • Интерактивные примеры, если применимо. Они могут быть переданы новым разработчиком для быстрого наблюдения за использованием и ожидаемым результатом вашей функции. А также вы можете использовать модуль doctest для подтверждения правильности этих примеров (запускаемых как тесты). Примеры см. в doctest documentation .
  • Документация по параметрам (обычно одна строка, описывающая параметр и его роль в функции)
  • Документация по типу возврата (если только ваша функция ничего не возвращает!)

Sphinx – это широко используемый инструмент для создания и управления вашей проектной документацией. Он предлагает множество удобных функций, которые уменьшат ваши усилия при написании стандартной документации. Кроме того, вы можете бесплатно опубликовать свою документацию на сайте ReadtheDocs , что является наиболее распространенным способом размещения документации для проектов. The Hitchhiker’s guide to Python for documentation содержит некоторую интересную информацию, которая может быть полезна вам при документировании вашего кода.

Практика 3: Ускорьте Свою Работу

Мультипроцессор, а не многопоточность

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

Чтобы добиться фактического распараллеливания в Python, вам, возможно, придется использовать модуль Python multiprocessing . Другим решением может быть аутсорсинг задач:

  1. Операционная система (путем выполнения мультипроцессорной обработки)
  2. Некоторое внешнее приложение, которое вызывает ваш код Python (например, Spark или Hadoop)
  3. Код, который вызывает ваш код Python (например, вы можете заставить свой код Python вызвать функцию C, которая делает дорогостоящие многопоточные вещи).

Помимо мультипрограммирования, есть и другие способы повысить производительность. Некоторые из них включают в себя:

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

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

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

  • Использование Cython : Cython-это язык Python надмножества, который позволяет пользователям вызывать функции C и иметь статические объявления типов, что в конечном итоге приводит к более простому конечному коду, который, вероятно, будет выполняться намного быстрее.

  • Использование PyPy : PyPy-это еще одна реализация Python, имеющая JIT-компилятор (just-in-time), который может ускорить выполнение вашего кода. Хотя я никогда не пробовал PyPy, он также утверждает, что уменьшает потребление памяти вашей программой. Такие компании, как Quora, фактически используют PyPy в производстве.

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

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

Практика 4: Выбор правильных версий!

Python2.x или Python3.x?

С одной стороны, Python3 имеет несколько замечательных новых функций. С другой стороны, вы можете использовать пакет, который поддерживает только Python2, например (Apple core ml tools . Кроме того, Python3 не имеет обратной совместимости. Это означает, что запуск кода Python2 на интерпретаторе Python3.x может привести к ошибкам.

Желательно использовать последнюю версию Python при запуске нового проекта, но если по какой-то причине вы должны придерживаться Python 2.x, то можно написать код таким образом, чтобы он работал как на интерпретаторах Python2, так и на интерпретаторах Python3. Наиболее распространенным способом является использование таких пакетов, как feature , built ins и six для поддержания единой, чистой кодовой базы, совместимой с Python3.x, которая поддерживает как Python2, так и Python3 с минимальными накладными расходами.

python-future – это отсутствующий уровень совместимости между Python2 и Python3. Он предоставляет пакеты future и past с бэкпортами и форвардными портами с функциями Python3 и Python2. Он также поставляется с futurize и pasteurize , настроенными скриптами на основе 2-to-3, которые помогают вам легко конвертировать код Py2 или Py3 для поддержки Python2 и Python3 в единую чистую кодовую базу Py3-стиля, модуль за модулем.

Пожалуйста, ознакомьтесь с отличной Шпаргалкой для написания кода, совместимого с Python 2-3, Эда Шофилда. Если вы больше любите смотреть видео, чем читать, вы можете найти его выступление на PyCon AU 2014 ” Написание совместимого кода 2/3 ” полезным.

Обработка ваших требований pip

Как правило, все зависимости pip проекта задаются в файле с именем requirements.txt в корне вашего проекта. Другой человек, пытающийся запустить ваш проект, может просто установить все требования с помощью этого файла с помощью команды pip install-r requirements.txt . Также распространенной практикой является размещение зависимостей, необходимых для запуска тестов, в отдельном файле с именем test-requirements.txt .

Обратите внимание, что pip не использует requirements.txt когда ваш проект установлен как зависимость другими. Как правило, для этого вам придется указать зависимости в аргументах install_requires и tests_require функции setuptools.setup в вашем setup.py файл. Если вы хотите поддерживать общий файл зависимостей как для упаковки, так и для разработки, вы можете сделать что-то вроде

import os
from setuptools import setup

with open('requirements.txt') as f:
    required = f.read().splitlines()

setup(...
    install_requires=required,
      ...)

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

Использование виртуальной среды

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

Версирование вашего проекта.

Следуйте Семантическому версионированию , руки вниз! См. это руководство для различных способов хранения версии проекта в вашем пакете.

Практика 5: Анализ кода

Часто бывает полезно проанализировать код на предмет покрытия, качества и производительности. Python поставляется с модулем cProfile для оценки производительности. Он не только дает общее время работы, но и умножает каждую функцию отдельно.

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

скриншот-от-2016-12-26-17-34-10
  • memory_profiler – это модуль Python для мониторинга потребления памяти процессами, а также построчного анализа потребления памяти программами Python.

  • objgraph позволяет вам показать верхние N объектов, занимающих память нашей программы Python, какие объекты были удалены или добавлены за определенный период времени, а также все ссылки на данный объект в вашем скрипте.

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

Практика 6: Тестирование и непрерывная интеграция

Тестирование: Хорошей практикой является написание модульных тестов. Если вы считаете, что написание тестов не стоит затраченных усилий, взгляните на этот поток StackOverflow. Лучше писать тесты до или во время кодирования. Python предоставляет модули unit test для написания модульных тестов для ваших функций и классов. Существуют такие фреймворки, как:

  • nose – может запускать unittest тесты и имеет меньше шаблонности.
  • pytest – также запускает unittest тесты, имеет меньше шаблонности, лучшую отчетность и множество интересных дополнительных функций. Чтобы получить хорошее сравнение между ними, прочтите введение здесь .

Не забывайте о модуле doctest , который тестирует ваш исходный код с помощью интерактивных примеров, проиллюстрированных во встроенной документации.

Измерение покрытия: Coverage – это инструмент для измерения покрытия программного кода Python. Он отслеживает вашу программу, отмечает, какие части кода были выполнены, а затем анализирует исходный код, чтобы определить код, который мог быть выполнен, но не был.

Измерение охвата обычно используется для оценки эффективности тестов. Он может показать, какие части вашего кода выполняются тестами, а какие нет. Часто рекомендуется иметь 100% – ное покрытие ветвей, что означает, что ваши тесты должны быть способны выполнять и проверять выходные данные каждой ветви проекта.

Непрерывная интеграция: Наличие системы CI для вашего проекта с самого начала может быть очень полезно для вашего проекта в долгосрочной перспективе. Вы можете легко протестировать различные аспекты вашей кодовой базы с помощью службы CI. Некоторые типичные проверки в CI включают:

  • Выполнение тестов в реальной среде. Бывают случаи, когда тесты проходят на одних архитектурах и терпят неудачу на других. Служба CI может позволить вам выполнять тесты на различных системных архитектурах.

  • Применение ограничений покрытия для вашей кодовой базы.

  • Создание и развертывание кода в рабочей среде (это можно сделать на разных платформах)

В настоящее время существует несколько сервисов CI. Некоторые из самых популярных из них-Travis, Circle (для OSX и Linux) и Appveyor (для Windows). Более новые, такие как SemaphoreCI , также кажутся надежными, согласно моему первоначальному использованию. Gitlab (другая платформа управления репозиторием Git, такая как Github) также поддерживает CI, хотя вам нужно будет настроить его явно, как и другие сервисы.

Обновление: Этот пост был полностью основан на моем личном опыте. Может быть, есть много вещей, которые я пропустил (или не знаю). Если у вас есть что-то интересное, дайте мне знать в комментариях. Кто-то запустил тему на ту же тему в HN, я бы рекомендовал вам проверить ее https://news.ycombinator.com/item?id=15046641 для более критического обсуждения этого поста. Я постараюсь ответить на все предложения и буду часто обновлять этот пост.