Журнал / польза

Python: простые, но полезные советы по оптимизации кода

Санитарная обработка данных, пропуск начала итерируемого объекта и другие приёмы.

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

 

Сделайте «санитарную» обработку входных данных эффективнее

Чем больше размер программы, тем выше шансы пропустить уязвимость в коде. Один из способов обезопасить себя от возможных ошибок — очистка входных данных перед выполнением программы (input sanitization). В большинстве случаев при таком подходе достаточно поменять регистр символов или использовать регулярные выражения. Но для сложных случаев есть и более эффективный способ:

Это простой пример: заменить «пробельные» символы \n и \t обычным пробелом и удалить \r (все перечисленные конструкции обозначают разные виды пробелов). В зависимости от задач, можно генерировать таблицы соответствий разного размера. Задачу облегчает пакет unicodedata и функция combining() для генерации и отображения. Их можно использовать для удаления всех акцентов из строки.

 

При необходимости используйте итератор со срезами

Итератор — это инструмент для поточной обработки данных. Он отвечает за упрощение навигации по элементам: списку, словарю и так далее. Это такой объект-перечислитель, который выдаёт следующий элемент. В основном его используют в цикле for.

Но использовать итератор на полную мощность нужно не всегда. И тут незадача: если вы попытаетесь использовать срез итератора (islice), то получите ошибку TypeError. Это произойдёт из-за того, что объект итератора не является подписываемым. К счастью, на такой случай есть простое решение:

Используя itertools.islice, можно создать объект islice, который сам по себе является итератором, производящим нужные нам значения. Важно отметить, что этот объект использует все элементы генератора вплоть до начала среза, что делает itertools.islice мощным инструментом.

 

Легко пропускайте начало итерируемого объекта

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

Этот фрагмент кода создаёт строки после начального комментария. Такой подход может быть полезен, если нужно отбросить элементы (в нашем случае строки) в начале итерируемого объекта.

 

Передавайте в функцию сколько угодно элементов — используйте kwargs

Если при разработке программы нужно выполнить несколько похожих действий, то лучшее решение — определить функции для многоразового использования кода. Для этого вызовите функцию с аргументом. Но что делать, если аргументы функции определены, а вам нужно передать больше значений? Для этого можно использовать kwargs — функции для именованных аргументов.

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

Как видно из примера, задачу легко решить, если поместить аргумент * перед ключевыми словами. И, конечно, можно использовать позиционные аргументы, если вставить их до аргумента *.

 

Используйте объекты, которые поддерживают оператор with

Открыть файл и заблокировать фрагмент кода можно с помощью оператора with, но можно ли сделать это, пользуясь собственным методом? Да, можно реализовать протокол context manager, используя методы __enter__ и __exit__:

Это распространённый вариант управления контекстом в Python, но есть и более простой способ:

Этот фрагмент кода реализует протокол управления контекстом, используя декоратор менеджера contextmanager. Первая часть функции tag (до yield) выполняется при входе в блок with, затем исполняется блок, а после него и остальная часть функции tag.

 

Сохраните всё с помощью __slots__ 

Если программа создаёт большое количество инстансов какого-либо класса, то она может потребовать больше памяти. Это связано с тем, что Python использует словари для представления атрибутов инстансов классов. Это делает язык быстрым, но не очень эффективным с точки зрения оптимизации памяти. Если это становится проблемой, то поможет функция __slots__:

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

Следует учесть, что в этом случае есть и недостатки: нельзя объявлять какие-либо новые атрибуты помимо используемых в __slots__, а классы со __slots__ не могут использовать множественное наследование.

 

Ограничьте использование процессора и памяти

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

Здесь можно увидеть две опции: установку на максимальное процессорное время и максимальный предел используемой памяти.

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

Что касается памяти, то, как и в случае с процессором, устанавливаем мягкий и жёсткий лимиты. Для этого используйте setrlimit с аргументом размера и извлеките жёсткое ограничение.

 

Управляйте экспортом элементов

Такие языки программирования, как Go, имеют механизм экспорта только для элементов (переменных, методов, интерфейсов) начинающихся с заглавной буквы. В Python подобного можно добиться с помощью переменной __all__:

В данном случае, благодаря __all__ экспортирован будет не весь код, а только функция __bar__. Кроме того, можно оставить переменную пустой, и ничего не попадёт в экспорт, что приведёт к ошибке AttributeError при попытке импорта из этого модуля.

 

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

Использовать все операторы сравнения для одного класса может быть довольно сложно, учитывая, что их немало: __It__, __le__, __gt__ или __ge__. Но есть ли более простой способ сделать это? Здесь поможет functools.total.ordering:

Как это работает? Декоратор total_ordering автоматически добавляет все остальные методы. В этом случае нужно только определить __It__ и __eq__, а все остальные пробелы за нас заполнит декоратор.

 

Заключение

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