6 Лучших практик Python, которые отличают Сениоров от Джуниоров
В январе 2023 года я опубликовал статью о 5 хитростях Python, которые отличают Сениоров от Джуниоров. В этой статье, вместо того чтобы рассматривать хитрости, мы рассмотрим 6 лучших практик в Python, которые могут отличить опытных разработчиков от новичков. На различных примерах мы рассмотрим различия между кодом, написанным старшим разработчиком, и кодом, написанным Джуниор-разработчиком.
Изучив эти рекомендации, вы сможете писать более качественный код, что, несомненно, будет большим плюсом для вас! Давайте начинать!
1. Используйте правильный итеративный тип данных
Iterable - это любой объект Python, способный возвращать свои элементы по одному за раз, что позволяет выполнять итерации по нему в цикле for. (источник)
Младшие разработчики, как правило, используют списки каждый раз, когда им нужен итеративный объект. Однако разные итеративные типы служат разным целям в Python. Вот наиболее важные из них:
- Списки предназначены для повторяющихся объектов, которые должны быть упорядочены и изменяемы.
- Множества предназначены для итераций, которые должны содержать только уникальные значения и являются изменяемыми и неупорядоченными. Им следует отдавать предпочтение при проверке на наличие товара, в которой они выполняются чрезвычайно быстро. Однако они работают медленнее, чем список, когда используются для перебора.
- Кортежи предназначены для итераций, которые должны быть упорядоченными и неизменяемыми. Кортежи работают быстрее и с большей экономией памяти, чем списки.
Давайте сначала взглянем на разницу при использовании множества по сравнению со списком. Представьте себе простую задачу предупреждения пользователя, когда запрошенное имя уже используется. Например, вы часто можете сталкиваться с таким кодом, как этот:
taken_usernames - это список в приведённом выше коде. Однако все значения в taken_usernames должны встречаться только один раз, нет необходимости в повторяющихся значениях, так как повторяющиеся имена пользователей не допускаются. Кроме того, вариант использования здесь для taken_usernames заключается в проверке наличия в нём нового имени пользователя. Следовательно, здесь нет причин использовать список. Вместо этого лучше использовать множество (это связано с тем, что проверка наличия выполняется быстрее при использовании множества и потому что нет необходимости сохранять одно и то же значение более одного раза).
Несмотря на то, что оба фрагмента кода в конечном итоге приводят к одному и тому же результату, использование множества для проверки наличия вместо списка показывает другим, что вы понимаете, что существуют разные итерируемые типы для разных случаев использования, вместо того, чтобы использовать список каждый раз, когда вам нужен итерируемый объект.
Для итеративных объектов, которые не будут изменяться во время выполнения и нуждаются в упорядочении, лучшим вариантом является кортеж; например:
Теперь у вас есть лучшее понимание того, когда использовать тот или иной итеративный тип данных! Ваш код уже не будет казаться устаревшим, когда вместо списка вы будете использовать множество для итерационных объектов, которые содержат только уникальные значения, и кортеж для упорядоченного итерационного объекта, значения которого не должны меняться.
В следующем разделе мы рассмотрим соглашения об именовании в Python, после чего станет ясно, почему, например, WEEKDAYS было написано именно заглавными буквами (в примере выше).
2. Используйте соглашения об именовании в Python
В Python существует два вида правил для имён переменных:
- Принудительные правила
- Соглашения об именовании
Принудительные правила предотвращают недопустимые имена переменных, такие как имена переменных, начинающиеся с цифры или содержащие дефисы:
Конечно, поскольку они применяются интерпретаторами Python, вы (надеюсь) не увидите, чтобы что-либо из них применялось в коде. Однако существуют рекомендации по стилю (также известные как соглашения об именовании) для имён переменных, которые не применяются, и, следовательно, вы можете использовать неправильный стиль для неправильного объекта.
Это некоторые из наиболее важных соглашений об именовании в Python:
переменные: только в нижнем регистре, которые подчёркиваются для разделения слов, например:
- first_name items names_list
функции и методы: то же правило, что и для переменных, например:
- get_avg load_data print_each_item
классы: используйте camelCasing; начинайте с заглавной буквы, и каждое новое слово начинается с другой заглавной буквы, без подчеркиваний между ними:
- Person TrainStation MarineAnimal
константы: только в верхнем регистре, с подчеркиванием для разделения слов, например:
- WEEKDAYS FORBIDDEN_WORDS
модули: для имён файлов Python используйте то же соглашение, что и для переменных, функций и методов (нижний регистр с подчёркиванием для разделения слов):
- calculations.py data_preprocessing.py
Использование правильных соглашений об именовании не только свидетельствует о зрелости Python, но и неиспользование правильных соглашений об именовании может привести к значительно более запутанному коду, как мы увидим ниже.
Код на Python, соответствующий соглашениям об именовании PEP-8:
Код на Python, который не использует соглашения об именовании:
Большинство разработчиков определённо сильно усомнятся в ваших навыках работы на Python, если вы предоставите им код во втором фрагменте, в то время как при предоставлении первого фрагмента они увидят качественный код на Python. Поэтому очень важно убедиться, что вы придерживаетесь соглашений об именовании в Python.
Для получения более подробной информации о соглашениях об именовании в Python см. PEP-8.
3. Используйте правильные инструкции для сравнения
Операторы сравнения (...) сравнивают значения по обе стороны от них и возвращают логическое значение. Они определяют, является ли утверждение истинным или ложным в зависимости от условия. (источник)
В Python существует много способов написания (почти) идентичных операторов сравнения, но они не обязательно одинаково подходят. Давайте взглянем на небольшой пример:
Важно отметить, что почти любая переменная Python будет иметь значение True, за исключением:
- Пустых последовательностей, таких как списки, кортежи, строки и т.д.
- Числа 0 (как в целочисленном, так и в плавающем типе)
- None
- False (очевидно)
Это означает, например, что каждый оператор if : будет иметь значение True, за исключением случаев, когда это число равно 0. Следовательно, оператор if без какого-либо конкретного примера может показаться слишком глобальным для использования, поскольку высока вероятность того, что он невольно может быть оценён как True. Тем не менее, мы можем очень хорошо использовать тот факт, что пустые последовательности всегда оцениваются как False, в то время как последовательность, имеющая хотя бы одно значение, всегда оценивается как True. Часто в более неосознанном Python вы столкнётесь со следующим оператором равнения: if <переменная> != [], например, в приведенном ниже фрагменте.
Однако, что произойдет, когда кто-то вставит другой итерируемый тип? Например, множество. Если вы хотите вызвать ValueError для пустого списка, вы, вероятно, также захотите вызвать ValueError для пустого множества. В приведённом выше коде пустое множество всё равно будет иметь значение True, потому что оно не равно пустому списку. Один из способов предотвратить такое нежелательное поведение - использовать if items вместо if items != [], потому что if items теперь будет вызывать ValueError для каждой пустой итерации, включая список, множество и кортеж из section1.
Если вы хотите сравнить значение с True или False , вам следует использовать is True или is False вместо == True и == False . Это также относится к None , потому что все они являются одиночными. Смотрите PEP285. Хотя различия в производительности незначительны, is True работает немного быстрее, чем == True. Прежде всего, это показывает, что вы знакомы с PEPs (предложениями по улучшению Python), что свидетельствует о зрелости навыков разработчика.
Бонусный совет: PEP8 предупреждает об использовании значения if, чтобы убедиться, что значение не равно None. Чтобы проверить, не равно ли значение None , используйте if value is not None.
Выбор наиболее подходящего оператора сравнения иногда может избавить вас или других пользователей от необходимости отлаживать сложную ошибку. Но, прежде всего, старшие разработчики оценят, что вы более равны им, если вы используете, например, if value is True, а не if value == True.
Конечно, вместо того, чтобы просто писать оператор сравнения для значения переменной, лучше сначала проверить тип данных, но как мы создаем хорошие исключения для этого? Давайте взглянем на создание информативных исключений в следующем разделе!
4. Создавайте информативные исключения
Что-то, что Джуниоры редко делают - это "вручную" создавать исключения с помощью пользовательских сообщений. Давайте рассмотрим следующую ситуацию: мы хотим создать функцию с именем print_each_item(), которая выводит каждый элемент итеративного типа.
Самым простым решением было бы:
Конечно, этот код работает. Однако, когда эта функция является частью большой базы кода, возможное отсутствие результатов вывода при запуске программы может сбивать с толку. Правильно ли вызвана функция? Одним из способов решения такой проблемы является использование логгирования, которое мы обсудим в следующем разделе. Во-первых, давайте посмотрим, как предотвратить такие проблемы, как отсутствие результатов вывода, путём создания исключений.
Функция print_each_item() работает только с итеративными объектами Python, поэтому нашей первой проверкой должно быть, может ли Python выполнять итерации по предоставленному аргументу, используя встроенную в Python функцию iter():
Пробуя функцию iter() для элементов, мы проверяем, возможно ли выполнять итерации по элементам. Конечно, также возможно проверить тип элементов через isinstance(items, Iterable), однако некоторые пользовательские типы Python могут не считаться итерируемыми. Мы добавляем .with_traceback здесь к исключению, чтобы предоставить больше контекста для отладки при возникновении ошибки.
Далее, когда мы подтвердим, что items является итерируемым, мы должны убедиться, что items не является пустым, чтобы предотвратить отсутствие результатов вывода:
Ваша функция, скорее всего, всё равно будет отклонена при проверке другими пользователями. Это потому, что она не содержит docstring или каких-либо намёков на типы, которые необходимы для высококачественного кода на Python.
5. Аннотации типов и docstrings
Аннотации типов были введены в Python 3.5. С помощью них вы можете подсказать, какой тип ожидается. Очень упрощённым примером может быть:
Указывая str в качестве аннотации типа, мы знаем, что предложение должно быть строкой, а не, например, списком со словами. Через -> str мы указываем, что функция возвращает объект типа string. Python не будет применять правильные типы (if не вызовет исключения, если вставлен объект другого типа), но часто IDE, такие как Visual Studio Code и PyCharm, помогают вам писать код, используя аннотации типа (см. скриншот далее в этом разделе).
Мы также можем применить это в нашем print_each_item() через:
Строки документации помогают объяснить фрагменты кода, такие как функции или классы. Мы можем добавить следующую строку документа в print_each_item(), чтобы другим пользователям и нам самим в будущем было абсолютно ясно, что делает эта функция:
Теперь, если мы пишем код, который использует print_each_item, мы видим, что появляется следующая информация:
Добавив подсказки по типам и docstrings, мы сделали нашу функцию намного более удобной для пользователя!
Примечание: может показаться, что писать длинную строку документа для такой простой функции - это немного излишне, и вы могли бы возразить, что иногда это так и есть. К счастью, когда функция не является секретной, вы можете легко попросить ChatGPT написать для вас очень точную и продуманную строку документа!
6. Используйте логирование
Есть несколько вещей, которые делают использование вашего кода намного приятнее для других, такие как аннотации типа и docstrings. Однако одной из наиболее важных, малоиспользуемых и недооценённых функций является logging. Хотя многие (младшие) разработчики считают логирование сложным или ненужным, запуск правильно протоколированной программы может иметь огромное значение для любого, кто использует ваш код.
Для того, чтобы сделать возможным логирование в вашем коде, требуется всего две строки:
Теперь вы можете легко добавить логирование, чтобы помочь, например, с отладкой:
Это также может действительно помочь другим разработчикам в отладке, регистрируя сообщения об ошибках:
Поскольку логирование - такая редкая функция, которую можно увидеть в коде более молодых разработчиков, добавление её в свой собственный делает вас уже (кажущимся) гораздо более опытным в Python!
Заключение
В этой статье мы рассмотрели 6 лучших практик Python, которые могут изменить представление о джуниор-разработчике. Конечно, есть ещё много факторов, которые отличают старших разработчиков от младших, однако, применяя эти 6 практик, вы определённо отличите себя (будь то на работе, на собеседовании по программированию или при создании пакетов с открытым исходным кодом) от других младших разработчиков, которые не применяют их!
Ресурсы
Итерируемые типы
Соглашение об именовании
Правильные инструкции для сравнения
Аннотации типа и docstrings
Логирование
Мемы
Статья была взята из этого источника: