You are currently viewing Декораторы на Python

Декораторы на Python

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

Свойства функций первого класса:

  • Функция-это экземпляр типа объекта.
  • Вы можете сохранить функцию в переменной.
  • Вы можете передать функцию в качестве параметра другой функции.
  • Вы можете вернуть функцию из функции.
  • Вы можете хранить их в структурах данных, таких как хэш-таблицы, списки, …

Рассмотрим приведенные ниже примеры для лучшего понимания.

Пример 1. Обработка функций как объектов.

# Python program to illustrate functions
# can be treated as objects
def shout(text):
	return text.upper()

print(shout('Hello'))

yell = shout

print(yell('Hello'))

Выход:

HELLO
HELLO

В приведенном выше примере мы присвоили функцию shout переменной. Это не вызовет функцию, вместо этого она берет объект функции, на который ссылается крик, и создает второе имя, указывающее на него, — крик.

Пример 2: Передача функции в качестве аргумента

# Python program to illustrate functions
# can be passed as arguments to other functions
def shout(text):
	return text.upper()

def whisper(text):
	return text.lower()

def greet(func):
	# storing the function in a variable
	greeting = func("""Hi, I am created by a function passed as an argument.""")
	print (greeting)

greet(shout)
greet(whisper)

Выход:

HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, i am created by a function passed as an argument.

В приведенном выше примере функция приветствия принимает в качестве параметра другую функцию (в данном случае крик и шепот). Функция, переданная в качестве аргумента, затем вызывается внутри функции greet.

Пример 3: Возврат функций из другой функции.

# Python program to illustrate functions
# Functions can return another function

def create_adder(x):
	def adder(y):
		return x+y

	return adder

add_15 = create_adder(15)

print(add_15(10))

Выход:

25

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

Декораторы

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

Синтаксис для декоратора: 

@gfg_decorator
def hello_decorator():
 print("Gfg")

'''Above code is equivalent to -

def hello_decorator():
 print("Gfg")
 
hello_decorator = gfg_decorator(hello_decorator)'''

В приведенном выше коде gfg_decorator является вызываемой функцией, добавит некоторый код поверх какой-либо другой вызываемой функции, функции hello_decorator и вернет функцию-оболочку.

Декоратор может изменить поведение:  

# defining a decorator
def hello_decorator(func):

	# inner1 is a Wrapper function in
	# which the argument is called
	
	# inner function can access the outer local
	# functions like in this case "func"
	def inner1():
		print("Hello, this is before function execution")

		# calling the actual function now
		# inside the wrapper function.
		func()

		print("This is after function execution")
		
	return inner1


# defining a function, to be called inside wrapper
def function_to_be_used():
	print("This is inside the function !!")


# passing 'function_to_be_used' inside the
# decorator to control its behaviour
function_to_be_used = hello_decorator(function_to_be_used)


# calling the function
function_to_be_used()

Выход:

Hello, this is before function execution
This is inside the function !!
This is after function execution

Давайте рассмотрим поведение приведенного выше кода, как он выполняется шаг за шагом, когда вызывается “function_to_be_used”.

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

# importing libraries
import time
import math

# decorator to calculate duration
# taken by any function.
def calculate_time(func):
	
	# added arguments inside the inner1,
	# if function takes any arguments,
	# can be added like this.
	def inner1(*args, **kwargs):

		# storing time before function execution
		begin = time.time()
		
		func(*args, **kwargs)

		# storing time after function execution
		end = time.time()
		print("Total time taken in : ", func.__name__, end - begin)

	return inner1



# this can be added to any function present,
# in this case to calculate a factorial
@calculate_time
def factorial(num):

	# sleep 2 seconds because it takes very less time
	# so that you can see the actual difference
	time.sleep(2)
	print(math.factorial(num))

# calling the function.
factorial(10)

Выход:

3628800
Total time taken in : factorial 2.0061802864074707

Что делать, если функция возвращает что-то или функции передается аргумент?

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

def hello_decorator(func):
	def inner1(*args, **kwargs):
		
		print("before Execution")
		
		# getting the returned value
		returned_value = func(*args, **kwargs)
		print("after Execution")
		
		# returning the value to the original frame
		return returned_value
		
	return inner1


# adding decorator to the function
@hello_decorator
def sum_two_numbers(a, b):
	print("Inside the function")
	return a + b

a, b = 1, 2

# getting the value through return of the function
print("Sum =", sum_two_numbers(a, b))

Выход:

before Execution
Inside the function
after Execution
Sum = 3

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

Цепные Декораторы

Проще говоря цепные декораторы означает украшение функции несколькими декораторами.

Пример: 

# code for testing decorator chaining
def decor1(func):
	def inner():
		x = func()
		return x * x
	return inner

def decor(func):
	def inner():
		x = func()
		return 2 * x
	return inner

@decor1
@decor
def num():
	return 10

print(num())

Выход:

400

Приведенный выше пример аналогичен вызову функции как:

decor1(decor(num))