Detailed explanation of decorators of python three major devices

  • 2021-12-09 09:39:54
  • OfStack

Catalog decorator Summarize

Decorator

There are two concepts to understand before talking about decorators:

Object reference: The object name is just a variable bound to the memory address

def func():   #  The function name is just a variable bound to the memory address        
	print("i`m running") 
#  This is the call to the                        
func()  # i`m running
#  This is an object reference, which refers to the memory address         
func2 = func  
print(func2 is func)  # True
#  Called by reference   
func2()  # i`m running
Closure: Define a function A, and then define a function B inside the function, and the B function uses the variables of the outer A function

def out_func():
	out_a = 10
	def inner_func(inner_x):
		return out_a + inner_x
	return inner_func

out = out_func()
print(out)  # <function out_func.<locals>.inner_func at 0x7ff378af5c10> out_func Returns the inner_func Memory address of 
print(out(inner_x=2))  # 12

The difference between decorators and closures is that the parameters of decorators are function objects, while the parameters of closures are ordinary data objects


def decorator_get_function_name(func):
	"""
	 Get the name of the running function 
	:return:
	"""
	def wrapper(*arg):
		"""
		wrapper
		:param arg:
		:return:
		"""
		print(f" Current running method name: {func.__name__}  with  params: {arg}")
		return func(*arg)
	return wrapper

@decorator_get_function_name
def test_func_add(x, y):
	print(x + y)

@decorator_get_function_name
def test_func_sub(x, y):
	print(x - y)

test_func_add(1, 2)
#  Current running method name: test_func_add  with  params: (1, 2)
# 3
test_func_sub(3, 5)
#  Current running method name: test_func_sub  with  params: (3, 5)
# -2

Commonly used such as authentication verification, for example, the author will be used for login verification:


def login_check(func):
    def wrapper(request, *args, **kwargs):
        if not request.session.get('login_status'):
            return HttpResponseRedirect('/api/login/')
        return func(request, *args, **kwargs)
    return wrapper
@login_check
def edit_config():
	pass

Execution logic inside the decorator:


"""
>  1. def login_check(func):  ==> Will login_check Function loaded into memory 
>  ....
>  @login_check  ==> This is already in memory login_check This function is executed! ; You don't have to wait edit_config() Instantiation call 
>  2.  Above example @login_check The following actions are performed internally: 
>	  2.1  Execute login_check Function and set the  @login_check  Below   Function (edit_config)  As login_check Function, namely: @login_check  Equivalent to  login_check(edit_config)
>     2.2  It will be executed internally: 
      def wrapper(*args):
          #  Calibration session...
          return func(request, *args, **kwargs)   # func Is a parameter, and at this time  func  Equal to  edit_config In this case, it is equivalent to edit_config(request, *args, **kwargs)
      return wrapper     #  Returned  wrapper , wrapper Represents a function object, not a function instantiation object 
      2.3  In fact, it is the original  edit_config  Function is stuffed into another 1 Among the functions , Another 1 Among the functions, you can do 1 Some operations; Re-execute edit_config
      2.4  That will be executed  login_check  Function return value ( That is  wrapper Object ) Re-assign this return value to a new  edit_config Namely: 
      2.5  New edit_config = def wrapper:
             #  Calibration session...
            return  Original edit_config(request, *args, **kwargs) 
>  3.  That is, new edit_config()=login_check(edit_config):wrapper(request, *args, **kwargs):return edit_config(request, *args, **kwargs)  It's a bit round. Let's look at the steps and understand them carefully. 
"""

Similarly, a function can be decorated with multiple decorators, and the execution order is from top to bottom


from functools import wraps
def w1(func):
	@wraps(func)
	def wrapper(*args, **kwargs):
		print(" This is the first 1 Checkout ")
		return func(*args, **kwargs)
	return wrapper

def w2(func):
	@wraps(func)
	def wrapper(*args, **kwargs):
		print(" This is the first 2 Checkout ")
		return func(*args, **kwargs)
	return wrapper

def w3(func):
	def wrapper(*args, **kwargs):
		print(" This is the first 3 Checkout ")
		return func(*args, **kwargs)
	return wrapper

@w2  #  This is actually w2(w1(f1))
@w1  #  This is w1(f1)
def f1():
	print(f"i`m f1, at {f1}")

@w3
def f2():
	print(f"i`m f2, at {f2}")
# ======================  Instantiation phase  =====================
f1()
#  This is the first 2 Checkout 
#  This is the first 1 Checkout 
# i`m f1, at <function f1 at 0x7febc52f5e50>
f2()
#  This is the first 3 Checkout 
# i`m f2, at <function w3.<lo

Some students may wonder why the f1 object prints " < function f1 at 0x7febc52f5e50 > ", the f2 object prints" < function w3..wrapper at 0x7febc52f5f70 > "(that is, as a result of step 2.5, the value assigned is wrapper object), which is related to the wraps decorator used by wrapper inside w1 and w2.

The function of wraps is to assign one attribute value of the modified function (that is, func inside) to the modifier function (wrapper), including meta-information and "function object", etc.

At the same time, the decorator can also accept parameters:


def decorator_get_function_duration(enable):
	"""
	:param enable:   Do you need to count the time spent on function execution 
	:return: 
	"""
	print("this is decorator_get_function_duration")
	def inner(func):
		print('this is inner in decorator_get_function_duration')
		@wraps(func)
		def wrapper(*args, **kwargs):
			print('this is a wrapper in decorator_get_function_duration.inner')
			if enable:
				start = time.time()
				print(f" Before the function executes: {start}")
				result = func(*args, **kwargs)
				print('[%s]`s enable was %s it`s duration : %.3f s ' % (func.__name__, enable, time.time() - start))
			else:
				result = func(*args, **kwargs)
			return result
		return wrapper
	return inner

def decorator_1(func):
	print('this is decorator_1')
	@wraps(func)
	def wrapper(*args, **kwargs):
		print('this is a wrapper in decorator_1')
		return func(*args, **kwargs)
	return wrapper

def decorator_2(func):
	print('this is decorator_2')
	@wraps(func)
	def wrapper(*args, **kwargs):
		print('this is a wrapper in decorator_2')
		return func(*args, **kwargs)
	return wrapper

@decorator_1 #  It is quite here :decorator_1(decorator_2(decorator_get_function_duration(enable=True)(fun)))
@decorator_2 # = decorator_2(decorator_get_function_duration(enable=True)(fun))
@decorator_get_function_duration(enable=True)  # = decorator_get_function_duration(enable=True)(fun)
def fun():
	time.sleep(2)
	print("fun  Finished execution ~ ")

fun()
# ======== enable=False ============
"""
this is decorator_get_function_duration
this is inner in decorator_get_function_duration
this is decorator_2
this is decorator_1
this is a wrapper in decorator_1
this is a wrapper in decorator_2
this is a wrapper in decorator_get_function_duration.inner
fun  Finished execution ~ 
"""
# ======== enable=True ============
"""
this is decorator_get_function_duration
this is inner in decorator_get_function_duration
this is decorator_2
this is decorator_1
this is a wrapper in decorator_1
this is a wrapper in decorator_2
this is a wrapper in decorator_get_function_duration.inner
 Before the function executes: 1634635708.648994
fun  Finished execution ~ 
[fun]`s enable was True it`s duration : 2.002 s 
"""

Summarize

This article is here, I hope to give you help, but also hope that you can pay more attention to this site more content!


Related articles: