首页 > 代码库 > 16.python全栈之路:装饰器详解

16.python全栈之路:装饰器详解

装饰器

一.装饰器的意义

  比如:以下函数是供我们调用的,但是我们需要在每个函数中都添加同一个功能,该如何做到呢?

方法一:在每个函数中都加上新加的功能代码块

def f1():
	print("新功能")
	print("F1")

def f2():
	print("新功能")
	print("F2")

def f3():
	print("新功能")
	print("F3")
	
def f4():
	print("新功能")
	print("F4")

 

方法二:使用函数式编程,将需要添加的部分写成一个函数

def XX():
	print("新功能")   #在里面可以修改,新增的功能


	
def f1():
	XX()
	print("F1")

def f2():
	XX()
	print("F2")

def f3():
	XX()
	print("F3")
	
def f4():
	XX()
	print("F4")

  

方法三:前两种方法都没有遵守我们的开放封闭原则,对于函数内部,我们一般不允许修改。所以我们引入了装饰器  

def outer(func):   
  def inner():
    print("添加在原函数之前")
    r = func()    #保留原函数的返回值
    print(‘添加在原函数之后‘)
    return r       #将保留的返回值返回回去
  return inner

@outer	           #等价于f1 = outer(f1)
def f1():
	print("F1")

def f2():
	print("F2")

def f3():
	print("F3")
	
def f4():
	print("F4")	
	
f1()                #执行函数,f1()已经等价于inner()了

 

二.简单装饰器的流程剖析

 

技术分享

 

三.带参数的装饰器

3.1

#一般装饰器,不能装饰有参数的函数
def outer(func):  #func为待装饰的函数
	def inner():  #从上往下执行,遇到def inner,就将函数扔进内存空间,但不执行
		print("执行原函数前添加的部分")
		r = func() #接收原函数的返回值
		print("执行原函数后添加的部分")
		return r   #返回原函数的返回值
	return inner  #返回inner给index
	
	

@outer	           #index = outer(index)
def index():
	print("非常复杂的功能")
	return True
	

index()

 

3.2:修改方案:根据参数的个数,我们主要得修改装饰器的内层函数

#如果待包装函数有参数
#需要如何修改装饰器呢
def outer(func):  
	def inner(a1, a2):  
		print("执行原函数前添加的部分")
		r = func(a1, a2)
		print("执行原函数后添加的部分")
		return r   
	return inner  
	
	

@outer	          
def index(a1, a2):
	print("非常复杂的功能")
	return a1 + a2
	
@outer
def home(s1, s2):
	print("非常复杂的功能")
	return "HOME"
	

ret1 = index(1, 2)  #应用了装饰器之后index变成装饰器的内层函数了
			 

ret2 = home(1, 2)			 

 

3.3:但是,如果不同的待装饰函数的参数个数不一致该怎么办呢?

修改方案:使用动态参数,*arg**kwargs扩大接收参数的形式。

def outer(func):  
	def inner(*arg ,**kwargs):  
		print("执行原函数前添加的部分")
		r = func(*arg ,**kwargs)  #内部让*argj元组或**kwargs字典吐出参数,传入原函数
		print("执行原函数后添加的部分")
		return r   
	return inner  
	
@outer
def home(s1):       #一个参数
	print("非常复杂的功能")
	return "HOME"

@outer	          
def index(a1, a2):  #两个个参数
	print("非常复杂的功能")
	return "INDEX"

@outer
def show(x1, x2, x3):#三个参数
	print("非常复杂的功能")
	return "SHOW"

	
ret1 = home(1)

ret2 = index(1, 2) 			 

ret3 = show(1, 2, 3)		

  

四.使用多装饰器装饰一个函数

应用场景:例如有100个函数,其中10个函数需要加上一部分功能,

而另外90个函数还需要加上额外得一部分功能,怎么做?

方法一

def outer0(func):  
	def inner(*arg ,**kwargs):  
		print("执行原函数前添加的部分")
		r = func(*arg ,**kwargs) 
		print("执行原函数后添加的部分")
		return r   
	return inner  
	
def outer1(func):  
	def inner(*arg ,**kwargs):  
		print("执行原函数前添加的部分(额外)")
		print("执行原函数前添加的部分")
		r = func(*arg ,**kwargs)  
		print("执行原函数后添加的部分")
		return r   
	return inner  
	
	
@outer0   #功能1
def home(s1):      
	print("非常复杂的功能")
	return "HOME"

@outer0	 #功能1        
def index(a1, a2): 
	print("非常复杂的功能")
	return "INDEX"


@outer1  #功能2
def show(x1, x2, x3):
	print("非常复杂的功能")
	return "SHOW"

	
ret1 = home(1)

ret2 = index(1, 2) 			 

ret3 = show(1, 2, 3)

缺点:装饰器outer2的代码与outer的代码有重复的部分,我们应该

尽量避免重复代码

 

方法二:改进版

def outer1(func):  
	def inner(*arg ,**kwargs):  
		print("执行原函数前添加的部分(1)")
		r = func(*arg ,**kwargs)
		print("执行原函数前添加的部分(4)")		
		return r   
	return inner  
	
def outer0(func):  
	def inner(*arg ,**kwargs):  
		print("执行原函数前添加的部分(2)")
		r = func(*arg ,**kwargs)  
		print("执行原函数后添加的部分(3)")
		return r   
	return inner

@outer0
def home(s1):     
	print("非常复杂的功能")
	return "HOME"
	
@outer1   #先执行这个函数的装饰功能
@outer0	  #再执行这个函数的装饰功能
def index(a1, a2):  
	print("非常复杂的功能")
	return "INDEX"

	
ret1 = home(1)
ret2 = index(1, 2) 

 

多重装饰器流程分析:(原函数的两次变化)

  没应用装饰器之前,原函数就是原函数

  应用第一个装饰器(outer0))之后,原函数变成了outer0函数的内层函数了,由func保留了原函数

  应用第二个装饰器(outer1)之后,outer0的内层函数也就是此时的原函数传入了第二个装饰器里面

去了,替换成第二个装饰器的内存函数,并有第二个装饰器的参数func保存了原函数。

装饰过程

技术分享

 

执行过程

技术分享

 

装饰次序

技术分享

 

 五.装饰器的应用

  单层装饰器:比如京东的网页,在进入我的购物车之前需要登录,这个登录就是一个装饰器

  多层装饰器:对于京东的用户来说,有大家公共的页面,也有对于钻石vip和白金vip不同的

        页面,所以在使用第一层登录装饰器之后,还需要加上额外的用户权限的装饰器

 

16.python全栈之路:装饰器详解