Декораторы в Python
Декораторы — это мощный инструмент Python для добавления пользовательских функций к классам и функциям. Они определены для классов и функций. Они определяются аналогично функциям, но с добавлением ключевого слова декоратора. В этой статье мы будем учиться использовать декоратор
Что такое декораторы?
Декоратор — это функция, присоединенная к другой функции или классу. Он принимает один или несколько аргументов, и его задача — изменить поведение исходной функции или класса.
Прежде чем мы начнем создавать декоратор, нам нужно понять основы функций, прежде чем мы углубимся в них.
Итак, давайте создадим наши функции.
def func1(): print("Called func1") func1()
Полученные результаты:
Called func1
Теперь вместо того, чтобы печатать func1 или вызывать func1, мы напечатаем func1 без скобок и посмотрим, что произойдет.
def func1(): print("Called func1") print(func1)
Вывод:
<function func1 at 0x000002CB9EEA3E20>
Вы можете увидеть какой-то случайный адрес памяти, что на самом деле означает следующее: func1
это объект, и поскольку это объект, мы можем передавать его по всей нашей программе. Чтобы лучше понять это, давайте создадим еще одну функцию с именем func2
.
def func1(): print("Called func1") def func2(f): f() func2(func1)
функция func2
была создана, и она принимает и вызывается аргумент f
, который вызывается ниже функции. Теперь, поскольку func1 является объектом, и мы можем представлять функции как объект, давайте посмотрим на вывод ниже.
Called func1
Вы можете ясно видеть, что вызывается func1, теперь, как это работает, это func1
объект, который представляет функцию func1()
, и мы можем передать его через параметры, мы можем сохранить их в переменных и так далее.
Приведенный выше пример является основным принципом, который нам нужно понять в Python, когда речь идет о функциях.
Функции оболочки
Теперь мы увидим некую функцию, называемую функциями-оболочками .
Давайте посмотрим на пример ниже.
def func1(func): def wrapper(): print("Started") func() print("Ended") return wrapper
По сути, эта функция делает то, что внутри нее определена другая функция с именем wrapper
, она выводит вывод с именем started
, вызывается функцией func
, которую мы передали в нашу, func1
и выводит другое значение, которое говорит Ended
. Эта функция возвращает wrapper
функцию, которая была определена внутри нашей исходной функции.
Давайте создадим еще одну функцию с именем, f
и что мы хотим сделать, так это всегда делать эту func1
функциональность нашей исходной функцией, используя только что созданную функцию с именем f
.
def func1(func): def wrapper(): print("Started") func() print("Ended") return wrapper def f(): print("Hello") func1(f)()
и у нас есть вывод:
Started Hello Ended
Это может быть немного сложно, но причина, по которой мы используем круглые скобки () для вызова нашей функции, заключается в том, что значение wrapper
является функцией. Чтобы лучше понять это, давайте воспользуемся печатью для вывода значения func1, чтобы вы могли его увидеть.
def func1(func): def wrapper(): print("Started") func() print("Ended") return wrapper def f(): print("Hello") print(func1(f))
и вы можете увидеть вывод:
<function func1.<locals>.wrapper at 0x000001C74A78ADD0>
Итак, это говорит нам о том, что когда вы вызываете func1
, у вас есть значение, которое на самом деле равно другой функции f
.
Именно это свойство на самом деле позволит нам то, что называется украшением функции.
Используя псевдонимы функций, мы также можем получить такой же вывод, как показано ниже. Давайте посмотрим на пример ниже.
def func1(func): def wrapper(): print("Started") func() print("Ended") return wrapper def f(): print("Hello") x = func1(f) x()
Вывод:
Started Hello Ended
Что на самом деле делает приведенный выше код, так это то, что мы установили x
переменную =
функцию , в func1
которую передан параметр f
. Итак, это псевдоним функции, изменение имени функции без изменения ее функциональности.
Вам может быть интересно, почему эта статья посвящена декораторам, а мы говорим больше о функциях, ну, если вы поняли то, что мы узнали до сих пор, то вы в значительной степени понимаете декораторы.
Итак, код выше строкой x = func1(f)
можно заменить декораторами. Давайте посмотрим код ниже.
def func1(func): def wrapper(): print("Started") func() print("Ended") return wrapper @func1 def f(): print("Hello") f()
Что делает приведенный выше код, так это автоматически пишет эту строку кода x = func1(f)
для нас каждый раз, когда мы звоним f
, и у нас все еще есть те же результаты.
Started Hello Ended
Теперь давайте изменим наш код слайдом, чтобы показать вам, что что-то не так с тем, как мы это делаем.
def func1(func): def wrapper(): print("Started") func() print("Ended") return wrapper @func1 def f(a): print(a) f("Hi")
а именно: обратите внимание, что происходит, когда мы добавляем параметр в нашу функцию, и вместо того, чтобы печатать, Hello
он будет печатать a
, и мы можем вызвать f
со значением"Hi"
Traceback (most recent call last): File "C:\Users\Bansikah\PycharmProjects\MyCompiler\decorator.py", line 15, in <module> f("Hi") TypeError: func1.<locals>.wrapper() takes 0 positional arguments but 1 was given
Это дает и ошибку, обратите внимание на нашу функцию-оболочку.
def wrapper(): print("Started") func() print("Ended") return wrapper
мы вызываем функцию func()
без каких-либо параметров, поэтому, чтобы исправить это, мы будем использовать args
и kwargs
в параметрах нашей функции-оболочки. Вы, наверное, видели это в своем уроке по Python, но если нет, то я собираюсь написать об этом еще одну статью.
def func1(func): def wrapper(*args, **kwargs): print("Started") func(*args, **kwargs) print("Ended") return wrapper @func1 def f(a): print(a) f("Hi")
Причина, по которой мы используем args
и, kwargs
заключается в том, что мы знаем, что наша функция-оболочка должна содержать определенное количество аргументов, и мы не знаем, являются ли эти аргументы keyword
аргументами или regular in-place
аргументами, все, что мы знаем, это то, что она должна иметь некоторые аргументы. На самом деле это позволяет нам иметь любое количество аргументов для любой конкретной оформленной функции, и это будет работать правильно.
Вывод:
Started Hi Ended
И мы получили желаемый результат, которого ожидали, даже если мы решим добавить еще одну переменную, как показано ниже.
def func1(func): def wrapper(*args, **kwargs): print("Started") func(*args, **kwargs) print("Ended") return wrapper @func1 def f(a, b=9): print(a, b) f("Hi")
Вывод:
Started Hi 9 Ended
это непрерывная работа, и это то, что args
и kwargs
может сделать для нас.
Декоративные функции
Последнее, о чем мы собираемся поговорить, — это возврат значений из декорированных функций, перед этим я просто хотел бы сказать следующее: если имена переменных или функций, которые мы используем, сбивают вас с толку, вы можете изменить их на свои собственные имена, которые вы поймет лучше.
До сих пор мы печатали значения в декорированных функциях, что мы хотели бы вместо этого вернуть эти значения? Что ж, это довольно легко сделать, мы собираемся сделать это, создав еще одну функцию, как в примере ниже.
def func1(func): def wrapper(*args, **kwargs): print("Started") val = func(*args, **kwargs) print("Ended") return val return wrapper @func1 def f(a, b=9): print(a, b) # f("Hi") @func1 def add(x, y): return x + y print(add(4, 5))
Что мы сделали в приведенном выше коде, так это то, что мы создали вызываемую функцию add
, которая принимает два параметра x
, а y
также мы создали переменную val
в функции-оболочке и вернули файл val
. Вам может быть интересно, почему мы просто не вернули вот func(*args, **kwargs)
так: return func(*args, **kwargs)
лучше сделать это так.
Вывод:
Started Ended 9
По сути, именно так мы можем возвращать значения из декорированной функции. В приведенном выше примере вы можете видеть, что декоратор можно использовать более чем для одной функции.
Для лучшего понимания давайте рассмотрим применение декораторов на реальных примерах.
#Python decorators - Example 1 def before_after(func): def wrapper(*args): print("Before") func(*args) print("After") return wrapper class Test: @before_after def decorated_method(self): print("run") t = Test() t.decorated_method()
Вывод:
Before run After
Еще один пример, декоратор таймера, очень распространенный пример, возможно, вы уже видели его раньше.
Python Decorators - Example 2 import time def timer(func): def wrapper(): before = time.time() func() print("Function took:", time.time() - before, "seconds") return wrapper @timer def run(): time.sleep(2) run()
Вывод:
Function took: 2.0019941329956055 seconds
он измеряет, сколько времени требуется для запуска функции.
И, наконец, log()
пример декоратора.
# Python Decorators - Example 3 import datetime def log(func): def wrapper(*args, **kwargs): with open("logo.txt", "a") as f: f.write("Called function with" + " ".join([str(arg) for arg in args]) + "at" + str(datetime.datetime.now()) + "\n") val = func(*args, **kwargs) return val return wrapper @log def run(a, b, c=9): print(a + b + c) run(1, 3, c=9)
Вывод:
13
и у нас есть этот файл наизнанку log.txt
.
Called function with1 3atCalled function with1 3at2023-06-05 01:00:47.423933 Called function with1 3at2023-06-05 01:02:38.287020
Вывод
Вот и все о декораторах в питоне — очень продвинутая и широкая тема в питоне, поздравляю, если вы все поняли, и это действительно поможет вам стать экспертом в питоне.