Советы и уловки

Почему декораторы в Python - чистый гений

Анализируйте, тестируйте и повторно используйте свой код, используя лишь символ @

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

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

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



Прежде всего: функции высшего порядка

Короче говоря, декораторы - это изящный способ работы с функциями высшего порядка. Так что давайте сначала посмотрим на них!

Функции, возвращающие функции

Допустим, у вас есть одна функция, greet() - она ​​приветствует любой объект, который вы ей передаете. Допустим, у вас есть еще одна функция, simon() - она ​​вставляет «Саймон» везде, где это необходимо. Как мы можем совместить их? Подумайте об этом за минуту, прежде чем смотреть ниже.

def greet(name):
    return f"Hello, {name}!"
def simon(func):
    return func("Simon")
simon(greet)

Результат - 'Hello, Simon!'. Надеюсь, это имеет для тебя смысл!

Конечно, мы могли просто позвонить greet("Simon"). Однако все дело в том, что мы, возможно, захотим наделить «Саймона» множеством разных функций. И если мы не будем использовать «Саймон», а будем использовать что-то более сложное, мы сможем сэкономить массу строк кода, упаковав его в функцию типа simon().

Функции внутри других функций

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

def respect(maybe):
    def congrats():
        return "Congrats, bro!"
    def insult():
        return "You're silly!"
    if maybe == "yes":
        return congrats
    else:
        return insult

Функция respect() возвращает функцию; respect("yes") возвращает функцию поздравлений, respect("brother") (или какой-либо другой аргумент вместо "brother") возвращает функцию оскорбления. Чтобы вызвать функции, введите respect("yes")() и respect("brother")(), как обычную функцию.

Понятно? Тогда все готово для декораторов!

Азбука декораторов Python

Функции с символом @

Давайте попробуем комбинацию двух предыдущих концепций: функция, которая принимает другую функцию и определяет функцию. Звучит ошеломляюще? Учти это:

def startstop(func):
    def wrapper():
        print("Starting...")
        func()
        print("Finished!")
    return wrapper
def roll():
    print("Rolling on the floor laughing XD")
roll = startstop(roll)

Последняя строка гарантирует, что нам больше не нужно звонить startstop(roll)(); roll() хватит. Вы знаете, каков результат этого вызова? Попробуйте сами, если не уверены!

Теперь, в качестве очень хорошей альтернативы, мы могли бы вставить это сразу после определения startstop():

@startstop
def roll():
    print("Rolling on the floor laughing XD")

Это делает то же самое, но с самого начала приклеивает roll() к startstop().



Дополнительная гибкость

Почему это полезно? Разве для этого не требуется столько же строк кода, сколько раньше?

В данном случае да. Но как только вы имеете дело с немного более сложными вещами, это становится действительно полезным. На этот раз вы можете переместить все декораторы (т.е. часть def startstop() выше) в отдельный модуль. То есть вы записываете их в файл с именем decorators.py и записываете что-то вроде этого в свой основной файл:

from decorators import startstop
@startstop
def roll():
    print("Rolling on the floor laughing XD")

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

Вы также можете вложить декораторы:

from decorators import startstop, exectime
@exectime
@startstop
def roll():
    print("Rolling on the floor laughing XD")

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

Это вложение было бы эквивалентно такой строке:

roll = exectime(startstop(roll))

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

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

Наконец, вы даже можете добавлять аргументы в ваши декораторы - например, @mydecorator(argument). Да, все это можно сделать без декораторов. Но затем я желаю вам много удовольствия от понимания вашего кода без декораторов, когда вы перечитаете его через три недели ...

Применение: там, где декораторы режут сливки

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

Время выполнения измерения

Допустим, у нас есть функция с именем waste time(), и мы хотим знать, сколько времени это займет. Ну просто используйте декоратор!

import time
def measuretime(func):
    def wrapper():
        starttime = time.perf_counter()
        func()
        endtime = time.perf_counter()
        print(f"Time needed: {endtime - starttime} seconds")
    return wrapper
@measuretime
def wastetime():
    sum([i**2 for i in range(1000000)])
wastetime()

Десяток строк кода и готово! Кроме того, вы можете использовать measuretime() в любом количестве функций.



Замедление кода

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

import time
def sleep(func):
    def wrapper():
        time.sleep(300)
        return func()
    return wrapper
@sleep
def wakeup():
    print("Get up! Your break is over.")
wakeup()

Позвонив wakeup() make, вы сделаете 5-минутный перерыв, после чего консоль напомнит вам вернуться к работе.

Тестирование и отладка

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

def debug(func):
    def wrapper():
        print(f"Calling {func.__name__}")
    return wrapper
@debug
def scare():
    print("Boo!")
scare()

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

Повторное использование кода

Это само собой разумеется. Если вы определили функцию decorator(), вы можете просто добавить @decorator в свой код. Честно говоря, я не думаю, что это может быть проще!

Обработка логинов

Если у вас есть функции, которые должны быть доступны только в том случае, если пользователь вошел в систему, это также довольно просто с декораторами. Я отсылаю вас к полному примеру для справки, но принцип довольно прост: сначала вы определяете функцию типа login_required(). Перед определением функции, требующей входа в систему, вы должны открыть @login_required. Я бы сказал, достаточно просто.



Синтаксический сахар - или почему Python такой милый

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

Если C ++ - апельсин, то Python - ананас: так же питательный, но в три раза слаще. Декораторы - лишь один из факторов.

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

Желаю много сладкого кода!