Практическое правило для разработчиков Python

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

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

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

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

Если вы хотите ознакомиться с первыми 20 рекомендациями, нажмите на ссылку ниже.



Я также привел краткое изложение рекомендаций первой статьи в нижней части этой статьи для справки.

Руководящие указания

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

1. Всегда отображать время в формате UTC

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

date_time_now = datetime.datetime.utcnow()

2. Используйте десятичный класс вместо класса с плавающей запятой

Если мы когда-нибудь добавим 1.1 и 2.2, мы получим 3.3000000000000003

Это связано с тем, что версии 1.1 + 2.2 не имеют точного представления в двоичной системе с плавающей запятой. Как следствие, вводится десятичный модуль.

Модуль decimal может использоваться для создания неизменяемого десятичного числа, которое может использоваться как знак, цифры коэффициента и показатель степени. Специальные значения, такие как бесконечность, -infinity и NaN, также могут быть созданы с использованием пакета decimal.

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

>>> Decimal(1) / Decimal(0)
Decimal('Infinity')
>>> Decimal(355) / Decimal(113)
Decimal('3.14159292')

3. Используйте concurrent.futures и класс ProcessPoolExecutor, чтобы воспользоваться преимуществами функций параллелизма Python.

Реализация асинхронного приложения с несколькими процессами может быть сложной задачей.

Python предлагает встроенный модуль concurrent.futures. Модуль предоставляет высокоуровневый интерфейс для асинхронного выполнения вызываемых объектов.

Кроме того, класс ProcessPoolExecutor использует пул процессов для асинхронного выполнения вызовов.

import concurrent.futures
import math

def compute_number(n):
    number = n**n**n**2**n
    return number

def main():
    data = [1000, 200000, 4000000]
with concurrent.futures.ProcessPoolExecutor() as executor:
        for number in data:
            print('%d is prime: %s' % (number, compute_number(number))

if __name__ == '__main__':
    main()

Если вы хотите глубже понять многопроцессорность, я настоятельно рекомендую прочитать эту статью.



4. Используйте диспетчер контекста.

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

Мы можем реализовать волшебные функции: __enter__ и __exit__ в классе и указать, что мы хотим делать, когда ресурс диспетчера контекста используется и уничтожается в этих функциях. Затем мы можем использовать диспетчер контекста с помощью with statement.

В результате диспетчеры контекста делают наш код более читабельным, чем использование операторов try / except / finally.

Мы можем использовать оператор with для использования диспетчера контекста:

with my_object as name_it:
     # execute operations of my_object
     pass

5. Всегда находите узкие места в вашей программе, профилируя приложение.

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

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

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

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

import cProfile
import functools
import pstats
import tempfile
def profile_me(func):
    @functools.wraps(func)
    def wraps(*args, **kwargs):
        file = tempfile.mktemp()
        profiler = cProfile.Profile()
        profiler.runcall(func, *args, **kwargs)
        profiler.dump_stats(file)
        metrics = pstats.Stats(file)
        metrics.strip_dirs().sort_stats('time').print_stats(100)
    return wraps

Этот декоратор принимает функцию и выводит статистику профилирования.

  • Метод pstats.Stats() производит необходимые метрики
  • Метод strip_dirs() удаляет лишний путь из всех имен модулей.
  • Затем мы использовали метод sort_stats(). Он сортирует показатели по столбцу времени.
  • Наконец, метод print_stats() распечатывает всю статистику.

Я рекомендую прочитать эту статью, чтобы лучше понять, как работает профилирование.



6. Только общедоступные функции unit test вашего класса.

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

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

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

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

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

7. Начните использовать tracemalloc.take_snapshot ().

Когда процесс Python выполняется, он выделяет блоки в памяти. Управление памятью в CPython в первую очередь основано на подсчете ссылок. Когда счетчик ссылок равен 0, объект, на который имеется ссылка, удаляется из памяти. Затем он освобождает место для других объектов.

Для круговых саморегулируемых объектов CPython реализовал детектор циклов. Он освобождает память, когда требуется, чтобы объекты Python были удалены сборщиком мусора.

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

tracemalloc module - важный модуль, который можно использовать в качестве инструмента отладки для отслеживания блоков памяти, выделенных Python. Его можно использовать, чтобы точно узнать, когда был выделен объект. Это известно как информация об отслеживании. Мы также можем использовать tracemalloc для поиска статики выделенного блока памяти по имени файла и номеру строки.

Чтобы использовать tracemalloc:

  • Установите для переменной среды PYTHONTRACEMALLOC значение 1 or use -X tracemalloc command line option.
  • Вызвать функцию tracemalloc.start ()
  • Вычислить различия между двумя снимками для обнаружения утечек памяти.
import tracemalloc
tracemalloc.start()


snapshot1 = tracemalloc.take_snapshot()
memory_leak_function()
snapshot2 = tracemalloc.take_snapshot()

top_stats = snapshot2.compare_to(snapshot1, 'lineno')

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

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

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

Например, мы часто находим create_trade_new(), create_trade2(), create_trade_ex() функций в устаревшем торговом приложении, которые являются модификациями create_trade() функции.

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

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

Я также рекомендую использовать для интерпретатора Python аргумент командной строки -W error. Это может быть полезно в автоматических тестах при регрессионном тестировании зависимостей.

9. Используйте memoryview как интерфейс с нулевым копированием без копирования базовых данных.

memoryview - это встроенный тип данных, который можно использовать, когда вам нужны подмножества двоичных данных, которые должны только поддерживать индексацию / сплайсинг без больших затрат памяти. Он предлагает быструю и линейную сложность. В отличие от _18 _ / _ 19_. для чтения и записи объектов нам не нужно копировать лежащие в основе данные.

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

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

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

Кодирование - это искусство. Обзор чужого чистого кода - отличный опыт. Чистый код обычно начинается с соответствующего именования файлов и классов. Я настоятельно рекомендую использовать typing module для аннотирования вашего кода. Кроме того, используйте библиотеки статического анализа в своей автоматизированной сборке, чтобы проверить качество вашего кода.

Я настроил свою среду IDE для автоматического форматирования кода и выполнения статического анализа перед фиксацией кода в репозитории. Это также способствует согласованности всей кодовой базы.

Библиотеки статического анализа могут обнаруживать ошибки раньше. Я рекомендую использовать библиотеки mypy и pylint . Это гарантирует, что каждый коммит в вашей кодовой базе будет проверяться на наличие ошибок, а ваш код будет высокого качества.

Наконец, это побудит вашу команду следовать единому шаблону во всей кодовой базе.

Я также рекомендую использовать black library для автоформатирования кода за вас.

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

Краткое содержание 1-й статьи

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

  1. Используйте f-строки вместо строк C-style и str.format
  2. Укажите кодировку при чтении текста из внешних источников
  3. Используйте оператор with или try / finally для очистки локального ресурса после использования
  4. Вывести исключения из Exception, а не из BaseException.
  5. Ограничьте предложение try абсолютным минимальным объемом кода.
  6. Будьте последовательны в заявлениях о возврате
  7. Используйте звезду, чтобы распаковать предметы
  8. Используйте nampedtuple для простых структур
  9. Не добавляйте в функции изменяемое значение параметра по умолчанию.
  10. Используйте декоратор оберток из встроенного модуля functools в ваших собственных декораторах.
  11. Используйте оператор моржа
  12. Когда ваши коллекции больше по размеру, возвращайте генераторы
  13. Избегайте использования встроенных вложенных словарей Python
  14. Создавайте функции вместо классов для очень простых структур
  15. Используйте @classmethod для определения альтернативных конструкторов
  16. Используйте декоратор @property для реализации пользовательских методов получения и установки.
  17. Используйте WeakKeyDictionary для предотвращения утечки памяти
  18. Избегайте нескольких процессов Python на машине с процессором / ядром
  19. Используйте метаклассы с умом.
  20. Используйте декораторы классов, чтобы украсить класс

Резюме

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