Разбор 7 ошибок Python

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

В данной статье мы рассмотрим 7 ошибок, которые могут допустить специалисты по данным при создании ПО, и сопроводим каждый пример надлежащим решением.

Ошибка №1. Сомневаетесь насчет указания версии зависимости 

В Python принято включать все зависимости в файл requirements.txt. Обычно я указываю версии пакетов с помощью ==.

# Пример requirements.txt

numpy == 1.0.0

Так можно быть уверенным, что вместе с моим пакетом pip (менеджер пакетов) установит и указанные версии зависимостей. 

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

В противном случае pip переустановит пакет с версией, закрепленной в файле requirements.txt, что может привести к непредвиденным ошибкам. 

(virtualenv) ~ » pip install -r requirements.txt
Collecting numpy
  Using cached numpy-1.19.4-cp38-cp38-macosx_10_9_x86_64.whl (15.3 MB)
Installing collected packages: numpy
  Attempting uninstall: numpy
    Found existing installation: numpy 1.18.2
    Uninstalling numpy-1.18.2:
      Successfully uninstalled numpy-1.18.2
Successfully installed numpy-1.19.4

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

numpy >= 1.18.2

Если же пакет работает с любой версией зависимости, то нет необходимости ее указывать:

numpy

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

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

Не используйте самую последнюю версию зависимости 

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

Ошибка №2. Упускаете из внимания конфликт зависимостей  

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

Что такое конфликт зависимостей? 

Это ситуация, при которой “пакет А” требует numpy==1.0, а “пакет В”  —  numpy==1.2. 

Как проверить наличие у пакета конфликта зависимостей? 

Выполнив pip check, вы получите информацию о конфликтах зависимостей в виртуальной среде: 

(virtualenv) ~ » pip check

tensorflow 2.2.1 has requirement numpy<1.19.0,>=1.16.0, but you have numpy 1.19.4.

Как автоматически решить проблему конфликта зависимостей? 

Помимо рip Python располагает еще одним менеджером пакетов, способным контролировать конфликты зависимостей. Речь идет о pipenv, который автоматически устанавливает версии таким образом, чтобы они не конфликтовали друг с другом. 

При первом знакомстве с pipenv сразу же становится понятно, насколько это полезный инструмент для разработок Python. Вместо requirements.txt pipenv предоставляет файл pipenv с версиями пакетов. 

При этом один из недостатков pipenv состоит в том, что создание файла pipenv занимает очень много времени. Именно по этой причине я отказался от этого менеджера пакетов и вернулся к pip. 

Как избежать конфликта зависимостей? 

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

Ошибка №3. Не применяете грамотно обслуживаемые пакеты 

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

Жизнеспособный репозиторий подразумевает, что он: 

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

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

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

Ошибка №4. Не структурируете код библиотеки с помощью логики 

Как правило, рекомендуется отделять логику кода, например логику сборки дерева поиска решений, от логики интерфейса, к примеру МО функции подстройки или прогнозирования. 

Познакомиться с этим шаблоном проектирования можно на таком прекрасном примере, как пакеты МО. Казалось бы, что все просто, но вот лично я не раз ошибался. 

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

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

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

Ошибка №5. Пренебрегаете тестами 

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

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

Написание тестов не означает, что вы должны добиться 100% покрытия кода, при котором тестируется каждая строка. Главное, чтобы тестами была покрыта основная функциональность. 

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

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

Ошибка №6. Не применяете непрерывную интеграцию 

Непрерывная интеграция (CI)  —  это практика разработки, при которой разработчики часто, желательно несколько раз в день, отправляют код в общий репозиторий, после чего каждое слияние можно проверить с помощью автоматизированной сборки и тестов. Хотя автоматизированное тестирование и не является строго обязательной частью CI, оно обычно подразумевается. 

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

Обычно я предпочитаю Travis CI, но есть и многие другие. GitHub и GitLab изначально поддерживают CI. 

Репозиторий получает зеленую отметку, свидетельствующую об успешном завершении всех проверок 

Еще одно преимущество CI заключается в том, что потенциальные пользователи пакета обязательно заметят активное рабочее состояние вашего репозитория, а будущие работодатели заинтересуются вашим богатым опытом в сфере ПО.

Ошибка №7. Не задумываетесь о том, где хранить модели МО 

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

Куда загружать модели? 

GitHub имеет расширение Large File Store (LFS), служащее для хранения больших двоичных файлов, таких как модели МО. Вы можете отметить файлы, предназначенные для отправки в данное хранилище, и Git сделает это за вас. 

В .gitattributes определите файлы для отправки в хранилище больших файлов  —  *.zip, как показано в следующем примере:

*.zip filter=lfs diff=lfs merge=lfs -text

Куда скачивать модели? 

Самым простым и при этом неверным решением является загрузка моделей в каталог пакета. А что если вы устанавливаете пакет в нескольких виртуальных средах? Тогда каждая из них будет содержать свою собственную копию модели. Это приведет к скорому заполнению жесткого диска, поскольку модели МО могут занимать по несколько гигабайт. 

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

Заключение 

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

В завершении статьи приведу цитату Элеоноры Рузвельт: 

“Учитесь на чужих ошибках. Вы никогда не сможете прожить достаточно долго, чтобы сделать их все своими руками.“

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Roman Orac: 7 Python Mistakes Data Scientists Should Avoid