Трюки и советы по Python, которые облегчат вашу жизнь

3
19909
Добавить в избранное

Python – это мощный язык общего назначения. Давайте погрузимся в советы по Python, а также изучение трюков, потоков и библиотек.

Введение

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

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

Изучение стандартных типов данных

1. collections.namedtuple

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

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

2. collections.defaultdict

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

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

С этим сниппетом будет проще писать, чем со стандартным dict. Еще легче только с defaultdict:

Кажется, что выглядит нормально, но на самом деле будет генерироваться исключение KeyError, потому что default[‘a’] – это dict, а не defaultdict. Нужно сделать defaultdict, который по умолчанию использует дефолтные словари.

Если вам нужен только счетчик по умолчанию, вы можете использовать класс collection.counter, который предоставляет некоторые удобные функции, такие как most_common.

3. Перечисление

Итерация по любому содержимому в Python проста (как и в любом другом языке) – простой цикл for:

Очень часто требуются одновременно индекс элемента, и сам элемент. Программисты используют len() и range() для перебора списка по индексу, но есть более простой способ.

Функция перечисления возвращает, как индекс, так и элемент.

Управление потоком

Управляющие конструкции – это for, while, if-elif-else и try-except. Советы по Python и правильное использование этих структур помогут в большинстве случаев.

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

1. Исключения

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

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

Сначала мы рассмотрим данные:

Теперь давайте используем советы по Python и изящно обернем commit:

Условие finally дает понять, что db.close() будет выполняться всегда. Оглядываясь назад, мы видим, что весь код, связанный с сохранением наших данных, оказался в хорошей логической группировке на том же уровне отступов. Редактируя этот код позже, нам будет легко увидеть, что все строки привязаны к commit.

2. Контекст и контроль

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

  • Попытка получить ресурс (файл, сетевое соединение, что угодно).
  • Если это не удается, очистить все, что осталось.
  • В противном случае выполнить действия на ресурсе.
  • Записать что произошло.
  • Программа завершена.

Имея это в виду, давайте рассмотрим еще некоторые советы по Python и второй пример с базами данных из последнего раздела. Мы используем связку try-except-finally, чтобы убедиться, что любая транзакция, которую мы начали, была либо закоммичена, либо отменена.

Наш предыдущий пример повторяет по шагам описанный выше алгоритм. Но как часто эта логика меняется? Не очень часто.

2.1 Менеджер контекста

Менеджер контекста упрощает защиту некоторого блока, настраивая ресурсы (контекст), необходимые блоку во время выполнения. В нашем примере нам нужна транзакция базы данных, которая будет:

  • Подключаться к БД.
  • Начинаться в начале блока.
  • Коммитить или откатывать в конце блока.
  • Очищать в конце блока.

Метод __enter __() очень простой, поэтому начнем с него.

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

Метод __init__  – это то место, где выполняется соединение с БД, и, если он не работает, блок вообще запускаться не будет.

2.2 Работа методов

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

Теперь мы можем использовать нашу DatabaseTransaction как диспетчер контекстов для блока с действиями. В этом блоке будут запускаться методы __enter __  и __exit __, обрабатывать подключение к БД и сбрасывать его, когда мы закончим.

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

3. Генераторы

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

Генераторы бывают разные.

3.1 Ключевые слова

Вместо возврата мы используем yield-слова, что делает генератор особенным. При вызове my_generator(‘thing’) вместо получения результата функции мы получаем объект генератора, который можно использовать везде, где вы могли бы использовать список или другой итератор.

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

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

3.2 Генератор Фибоначчи

Теперь давайте создадим генератор, который немного полезнее, чем просто возврат трех жестко закодированных элементов. Пример классического генератора – бесконечный генератор Фибоначчи, который мы попробуем сделать. Он начнется с 1 и возвращает сумму двух предыдущих чисел столько раз, сколько вы попросите.

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

Теперь давайте используем наш генератор, чтобы посчитать первое число Фибоначчи, которое превышает 10 000.

Это было довольно просто, и мы можем сделать это число настолько большим, насколько захотим, и код все равно найдет первое число, большее X в последовательности Фибоначчи.

3.3 Пример с API

Теперь попробуем более реальный пример. В API пагинации есть распространенная практика ограничения и предотвращения отправки более 50 мегабайт при помощи JSON на мобильное устройство. Сначала мы объявим API, с которой работаем, а потом напишем генератор.

API, которое мы используем, называется Scream – это сервис, где пользователи могут обсуждать и оставлять отзывы о ресторанах, в которых они были. Оно выглядит так:

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

Генератор имеет ограниченную логику и обрабатывает пагинацию примерно так:

  • Получает поисковой запрос.
  • Запрашивает API-интерфейс scream-about-food.
  • Повторяет попытку, если API не работает.
  • Получает результаты со страницы.
  • Получает следующую страницу, если есть.
  • Выходит, когда результатов больше нет.

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

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

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

Генератор обрабатывает итерации на каждой странице поискового результата. Все, что нам нужно, – использовать встроенное перечисление, описанное ранее в статье, чтобы отслеживать количество результатов и выводить их.

В качестве упражнения добавим счетчик к бесконечному поиску infinite_search:

Если вы пишете на Python 3, то уже применяли генераторы при использовании стандартной библиотеки. Вызовы вроде dict.items() теперь возвращают генераторы вместо списков. Чтобы получить это поведение в Python 2, был добавлен dict.iteritems(), но он используется не так часто.

Совместимость Python 2 и 3

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

Подробное руководство по поддержке обеих версий смотрите в гайде на python.org.

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

1. print или print()

Почти каждый разработчик, который переключился с Python 2 на 3, использовал неверный оператор печати. К счастью, вы можете стандартизировать использование print как функции (в стиле Python 3): просто импортируем print_function.

2. Деление

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

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

Оригинал

Другие материалы по теме:

Интересуетесь программированием на Python?

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

И не беспокойтесь, мы тоже не любим спам. Отписаться можно в любое время.




3 Комментарии

Оставьте комментарий