Python 中创建和使用装饰器

441 阅读4分钟

一等对象
在Python中,函数是一等对象,这意味着Python中的函数可以被使用或作为参数传递。
一等函数的属性:
1、函数是Object类型的一个实例。
2、你可以将函数存储在变量中。
3、你可以将函数作为参数传递给另一个函数。
4、你可以从函数中返回函数。
5、你可以将它们存储在哈希表、列表等数据结构中……

考虑以下示例以更好地理解。
Example 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 分配给了一个变量。这不会调用函数,而是取函数对象 shout 所引用的,并创建一个指向它的第二个名称,yell。
Example 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 函数接受另一个函数作为参数(本例中为 shout 和 whisper)。作为参数传递的函数随后在 greet 函数内部被调用。
Example 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”时,它是如何一步步运行的。

image.png

image.png
让我们跳到另一个例子,其中我们可以很容易地使用装饰器找出一个函数的执行时间。

# 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

在上述示例中,您可能会注意到内部函数的参数存在明显差异。内部函数接受参数 *args 和 **kwargs,这意味着可以传递任意长度的位置参数元组或关键字参数字典。这使得它成为一个通用的装饰器,可以装饰具有任意数量参数的函数。

链式装饰器

用更简单的话说,链式装饰器意味着用多个装饰器来装饰一个函数。

# 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

@decor
@decor1
def num2():
	return 10

print(num()) 
print(num2())

输出:

400
200

上述例子类似于调用函数的方式——

decor1(decor(num))
decor(decor1(num2))