20 простых советов по настройке производительности Python. Часть II

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

11. Вовремя обновляйте ваш Python

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

12. Для бесконечных циклов используйте while 1

Если вы прослушиваете сокет, вам, вероятно, надо использовать бесконечный цикл. Обычный способ достижения этой цели конструкция while True. Это работает, но того же эффекта можно добиться немного быстрее, использовав while 1. Так как здесь есть число, то это единичная операция.

13. Попробуйте другой способ

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

14. Ранний выход

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

if positive_case:
    if particular_example: 
        do_something
else:
    raise exception

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

if not positive_case:
    raise exception
if not particular_example:
    raise exception
do_something 

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

15. Изучите библиотеку itertools

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

Обязательно изучите ее документацию, чтобы максимально использовать все возможности данной библиотеки. Вот например, функция перестановок. Допустим, вы хотите сгенерировать все перестановки списка  [“Alice”, “Bob”, “Carol”].

import itertools
iter = itertools.permutations(["Alice", "Bob", "Carol"])
list(iter)

Эта функция вернет все возможные перестановки:

[('Alice', 'Bob', 'Carol'),
 ('Alice', 'Carol', 'Bob'),
 ('Bob', 'Alice', 'Carol'),
 ('Bob', 'Carol', 'Alice'),
 ('Carol', 'Alice', 'Bob'),
 ('Carol', 'Bob', 'Alice')]

Это действительно полезно и невероятно быстро!

16. Используйте декоратор кэширования

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

Попробуйте сами вычислить сотое число в последовательности Фибоначчи. Если вы никогда раньше не сталкивались с этой последовательностью, то знайте, что каждое число в ней является результатом суммы двух предыдущих членов этой последовательности. Фибоначчи был итальянским математиком, который обнаружил, что эти числа всплывают в совершенно разных местах. От числа лепестков на цветке до ножек насекомых или веток на дереве — эти числа весьма распространены в природе. Первые пять чисел данной последовательности — 1, 1, 2, 3, 5.

Вот один из алгоритмов для вычисления членов последовательности Фибоначчи:

def fibonacci(n):
    if n == 0: # There is no 0'th number
        return 0
    elif n == 1: # We define the first number as 1
        return 1
    return fibonacci(n - 1) + fibonacci(n-2)

Когда я использовал этот алгоритм для вычисления 36 числа Фибоначчи, fibonacci(36), мой компьютер загудел, как будто он собрался взлететь! Вычисления длились пять секунд и результат равнялся (если вам любопытно!) 14,930,352.

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

import functools

@functools.lru_cache(maxsize=128)
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    return fibonacci(n - 1) + fibonacci(n-2)

В Python декораторы, сами являющиеся функциями, принимают в качестве аргумента другую функцию и расширяют ее функциональность. Для обозначение декоратора используется символ «@«. В данном примере мы использовали в качестве декоратора функцию functools.lru_cache, которая находится в модуле functools. В качестве аргумента мы передали в нее максимальное число членов последовательности, которые должны храниться в кэше. Здесь возможно применить и другие декораторы, в том числе и написанные вами лично, но эта функция уже имеется в стандартной библиотеке и работает весьма быстро. Насколько быстро? Вычисления были произведены за 0.7 секунды и, разумеется, результат был тот же самый.

17. Используйте ключи для сортировки

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

import operator
my_list = [("Josh", "Grobin", "Singer"), ("Marco", "Polo", "General"), ("Ada", "Lovelace", "Scientist")]
my_list.sort(key=operator.itemgetter(0))
my_list

Этот код отсортирует список по первому элементу кортежей, входящих в список:

[('Ada', 'Lovelace', 'Scientist'),
 ('Josh', 'Grobin', 'Singer'),
 ('Marco', 'Polo', 'General')]

Можно так же легко отсортировать и по второму элементу:

my_list.sort(key=operator.itemgetter(1))
my_list

Этот код вернет следующий список:

[('Josh', 'Grobin', 'Singer'),
 ('Ada', 'Lovelace', 'Scientist'),
 ('Marco', 'Polo', 'General')]

Можно легко заметить, что он отсортирован по второму элементу кортежей.

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

18. Не создавайте множества внутри условного оператора

Возможно, когда-нибудь вы решите оптимизировать ваш код вот таким образом:

if animal in set(animals):

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

if animal in animals:

Даже если окажется, что в списке значительно больше зверей для проверки, интерпретатор настроен таким образом, что применение функции set только замедлит выполнение. Проверка при помощи оператора in как правило быстрее без использования функции set.

19. Используйте связанные списки

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

Массивы требуют выделения памяти заранее. А это может быть затратным и бесполезным, особенно если мы не знаем размер массива.

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

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

20. Используйте облачные средства отладки

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

Заключение

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

Конечно, никакой список советов не будет думать вместо вас. Наш список призван указать на некоторые распространенные ошибки и заставить вас задуматься о вашем коде. И он также должен побудить вас постоянно задумываться о производительности и дизайне ваших приложений, и постоянно спрашивать себя о том, что может сделать работу кода еще быстрее и эффективнее.