Знайте идиомы Python и их альтернативы

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

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

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

1. Строки документации

Строки документации повторяют множество знаний, что является явным нарушением принципа DRY. Вещи, которые повторяются в строке документации:

  1. Описание аргументов и их типов Python, а также того, что возвращает функция - это уже есть в сигнатуре функции, если вы включаете аннотации типов.
  2. Краткое описание того, что делает функция - это уже есть в имени функции и теле функции
  3. Исключения, которые вызывает функция - это уже в теле функции

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

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

  • Дублирование знаний (объяснение или код) может привести к возможной несогласованности, если вы забудете изменить строку документации вместе с кодом.
  • Это дает разработчикам возможность исправить плохой код, написав строки документации.
  • Кажется, что некоторые описания в строке документации существуют для того, чтобы ее написать (просто потому, что это требуется для строки документации), например, объясняют, что она возвращает - да, это предварительно обработанный текст, нам не нужно об этом знать.

Что мы можем сделать вместо этого?

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

Исключение: библиотеки.

Однако строки документации необходимы в библиотеках, пакетах или фреймворках. Почему это нормально?

У библиотеки есть 2 разных типа аудитории: (1) участники кода и (2) другие разработчики, использующие их библиотеку.

В базе кода вашего проекта есть только первые - вы и ваши товарищи по команде, которые вносят свой вклад.

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

Ответственность за поддержание этих дополнительных накладных расходов на поддержание согласованности между документацией и кодом ложится на участников. Библиотеки обычно имеют в своем конвейере CI / CD проверки, чтобы убедиться, что документация соответствует коду. К ним относятся сложные модульные тесты, их запуск при наличии примеров кода (scikit-learn) и ручные проверки в обзорах кода (возможно, с несколькими рецензентами) перед интеграцией новых изменений.

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

2. Словари для данных только для чтения.

Словари Python - не лучший выбор для хранения данных «ключ-значение» только для чтения (например, конфигураций). Они не обеспечивают безопасность, потому что:

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

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

Что мы можем сделать вместо этого?

Используйте замороженные классы данных (Python 3.7), средства проверки статического типа (например, mypy) и линтеры.

Это создает класс с именем Person. frozen=True защищает нас от случайной перезаписи данных (поднятых mypy). И даже нет возможности получить доступ к атрибуту с ошибкой. Мы также извлекаем выгоду из автозаполнения атрибутов класса в обычных IDE.

Есть и другие альтернативы, хотя я их не рекомендую: UserDict (много шаблонного кода), класс с настраиваемыми установщиками (много шаблонного кода) и namedtuple (не выглядит таким интуитивно понятным, как класс данных) .

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

3. Строки вместо перечислений

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

  1. Риск орфографической ошибки.
  2. Избегайте проверок полноты из линтеров. Если вы забыли добавить параметр, mypy, возможно, обнаружит его за вас (см. Ниже).
  3. Дублирование кода. Если вы хотите повторно использовать, скажем, проверки ветки if-else, вам, возможно, придется повторить инструкции if-else.

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

Что мы можем сделать вместо этого?

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

К сожалению, проверка полноты из mypy возможна только путем написания дополнительного кода. См. Здесь (когда я в последний раз пробовал это, mypy по-прежнему не предупреждал).

4. Составьте список понятий, охватывающих несколько строк.

Хотя понимания (названные так, чтобы включать в себя понимание списка, словаря и генератора) довольно лаконичны, они могут стать очень нечитаемыми, если в коде есть более одного из следующих элементов:

  • if-else предложение (я)
  • несколько строк
  • вложенное понимание

Читаемость ухудшается, особенно для тех, кто предпочитает читать код последовательно.

Выглядит нормально:

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

И это самое худшее. Лаконично? Нет, спасибо.

Что мы можем сделать вместо этого?

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

На этом пока все! Спасибо за прочтение.

использованная литература

Прагматичный программист

Спасибо, Дэвид Чонг и Леонг Вей Ци за просмотр этой статьи!