Python Decorator装饰器|基础速览|难点讲解

一言一概之:装饰器是对函数的函数。

在Python中,一切皆对象,函数func本身也可被视为一个对象。而装饰器decorator就是将其他函数func视为自身input的特殊函数。即decorator( func() )
装饰器使得我们不需要破坏性地直接改变函数代码,而是通过添加装饰器,来达到改变/增加函数功能的作用。

装饰器的基本结构

def decorator(func):
	print('前置步骤')
	def wrapper(*args,**wargs):
		print('func前执行的步骤')
		func(*args,**wargs)
		print('func后执行的步骤')
	return wrapper

@decorator
def func(a,b):
	pass
	return

装饰器的难点

1.多个装饰器

这里引用python装饰器详解给出的例子

def dec1(func):  
    print("1111")  
    def wrapper1():  
        print("2222")  
        func()  
        print("3333")  
    return wrapper1 
  
def dec2(func):  
    print("aaaa")  
    def wrapper2():  
        print("bbbb")  
        func()  
        print("cccc")  
    return wrapper2 
 
@dec1  
@dec2  
def test():  
    print("test test")  
  
test()  

输出为:

aaaa  
1111  
2222  
bbbb  
test test  
cccc  
3333

多个装饰器的运行逻辑如下:

  1. 运行dec1(dec2(test()))(最“上方”的装饰器在最外侧运行)
  2. test作为对象,传参至dec2内。先执行dec2
  3. dec2的装饰器前置步骤被执行。输出aaaa
  4. dec2装饰器return wrapper2,并将后者传参之dec1
  5. dec1的装饰器前置步骤被执行。输出1111
  6. dec1装饰器return wrapper1。至此所有传参已经完成,开始依序执行wrapper1
  7. 输出2222。执行wrapper2wrapper1中的func实则为wrapper2
  8. 输出bbbb,执行func
  9. 依次输出cccc3333

概括来说,要点就是:

  • 装饰器由上至下,执行顺序由外至内。
  • 装饰器前置代码(wrapper前的代码块)会在传参前被执行。顺序为由内至外。
  • 装饰器wrapper内函数执行顺序为:外中内func内中外

2.closure

理解closure前先要理解scope
在Python中,变量范围被划分为global、non-local、local三种。

#global
def parent():
	#non-local
	def child():
		#local

对于装饰器而言,在non-local中创建的变量会被储存至child.__closure__中。
这意味着在如下代码中:

def decorator(func):
	num = []
	def wrapper(*args,**wargs):
		if func(*args,**wargs) == 1:
			num.append( (*args,**wargs) )
	return wrapper

@decorator
def func1():
	pass

@decorator
def func2():
	pass

虽然我们是分别对func1和func2添加了装饰器,从直觉上两个装饰器应该相互独立。但事实上,列表num始终储存在装饰器的closure中,并实时更新。从而num列表同时记录了func1func2的相关数据。

3.装饰器导致的metadata缺失

假如我们有如下函数:

def func():
	'''
	This is a function.
	'''
	pass
	return

print(func.__doc__)将会输出This is a function.
我们增加了装饰器dec

@dec
def func():
	'''
	This is a function.
	'''
	pass
	return

print(func.__doc__)将会输出None
print(func.__name__)将会输出wrapper
意识到问题了!装饰器将导致原函数的被替换为了包含了原函数的wrapper,使得metadata被覆盖
解决的办法是使用functooslwraps并添加至装饰器内部

from functools import wraps

def dec(func):
	@wraps(func)
	def wrapper():
		pass
	return wrapper()

@dec
def func():
	'''
	This is a function.
	'''
	pass
	return

4.可传入变量的装饰器

假如我们编写一个装饰器sleep_for(n)
它可以根据我们传入的参数n,来让目标函数延后n秒执行。

如何实现?一言以蔽之:
sleep_for(n)编写为输出一个装饰器的函数
代码如下:

def sleep_for(n):
	def decorator(func):
		def wrapper(*args,**wargs):
			time.sleep(n)
			func(*args,**wargs)
		return wrapper
	return decorator

@sleep_for(3)
def func1():
	pass
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值