python装饰器

装饰器

在python中,装饰器是一个可调用的对象,它接受一个函数参数(即需被装饰的函数),然后返回一个新的函数

假如我们有一个名为decor的装饰器函数,有个函数f需要被decor装饰,那么语法如下

@decor
def f():
	print("f function")

在上述代码中,其实际上等价于

def f():
	print("f function")

f = decor(f)

这样看来,python的装饰器实际上本身就是一个函数,它的特别之处在于它接受一个函数对象作为参数,然后返回另外一个函数

装饰器的定义

定义一个装饰器的方法与一般的函数定义一般无二,区别在于装饰器返回的是一个函数对象,例如,你可以什么都不做,直接返回被装饰的函数

def decor(func):
	return func

你也可以在被装饰的函数基础上做一些其他操作,比如,在装饰器内部定义一个函数inner,调用被装饰的函数,做一些其他事情,比如

def decor(func):
	def inner():
		print("inner function")
		func()
	return inner

装饰带有参数的函数

在上面这个例子中,函数f不带有任何参数,对于带有参数的函数f,该装饰器就不能正常工作了,那么,如何让装饰器也能装饰带有参数的函数呢

实际上,装饰器返回的是一个函数对象,既然是一个函数对象,我们只需要让这个返回的函数对象支持带有参数即可,我们改变上面的的装饰器,例如

def decor(func):
	def inner(a):
		print("inner function")
		func(a)
	return inner

@decor
def f(a):
	print("f function, a = %d" % a)

f(12)
inner function
f function, a = 12

在这里,当我们执行f(12)时,实际上是调用了decor内的inner函数,附带的参数12实际上被传递给了inner函数,inner函数首先执行print函数,然后调用真正的f(12)

装饰带有任意参数的函数

为了让我们的装饰器能够支持任意参数,只需要让inner支持任意参数即可,我们装饰器函数看起来是这样子

def decor(func):
	def inner(*args, **kwargs):
		print("inner function")
		func(*args, **kwargs)
	return inner

@decor
def f(a):
	print("f function, a = %d" % a)

@decor
def g(x, y, lst, dic):
	print(x, y, lst, dic)


f(12)
g('ddd', 90, [1, 43, 32], {'a': 434})

让装饰器带有参数

你可以让你的装饰器带有自己的参数,让装饰器本身带有自己的参数可以极大增加灵活性,例如,我可能有多个函数,它们都被相同的装饰器所装饰,但是对被装饰的函数,我想让它们的执行逻辑有些细微的不同,或者是纯粹的将参数作为一个标识,在这种情况下,我们的装饰器看起来是这样子

@w("i am f")
def f(a):
	print("f function, a = %d" % a)

为定义这种类型的装饰器,我们只需要在原来的装饰器上包裹一层函数,他接受一个参数param,例如

def w(param):
	def decor(func):
		def inner(*args, **kwargs):
			print("inner function")
			func(*args, **kwargs)
		return inner
	return decor

此时,被装饰的函数f等价于

f = w('i am f')(f)

装饰带有返回值的函数

在上面的所有例子中,被装饰的函数f都是不带返回值的,有时候,我们的确需要被装饰的函数带有返回值,那么怎么做呢

想一下,文章开头的decor装饰器返回的是inner函数,当我们装饰函数f时,实际上是将f重新指向了返回的inner函数对象,所以,如果原来的f本身带有返回值,只需要在inner函数的末尾返回f的结果即可,我们重写decor函数,并将其装饰一个add函数,如下

def decor(func):
	def inner(*args, **kwargs):
		print("inner function")
		result = func(*args, **kwargs)
		return result
	return inner

@decor
def add(x, y):
	return x + y

res = add(1, 23)
print(res)
inner function
24

Python何时执行装饰器

装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。这通常是在导入时(即 Python 加载模块时)

我们考虑这样一个例子(注:例子来自于《流畅的python》)

registry = []

def register(func):
	print('running register(%s)' % func)
	registry.append(func)
	return func

@register
def f1():
	print('running f1()')

@register
def f2():
	print('running f2()')

def f3():
	print('running f3()')

def main():
	print('running main()')
	print('registry ->', registry)
	f1()
	f2()
	f3()

if __name__=='__main__':
	main()

在这个例子中,当其作为脚本运行时,结果如下

running register(<function f1 at 0x00000209E8B93CA8>)
running register(<function f2 at 0x00000209E8CE1678>)
running main()
registry -> [<function f1 at 0x00000209E8B93CA8>, <function f2 at 0x00000209E8CE1678>]
running f1()
running f2()
running f3()

如果把它作为模块导入时,结果为

running register(<function f1 at 0x0000016471470F78>)
running register(<function f2 at 0x0000016471470288>)

上面这个例子说明,函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。这突出了 Python 程序员所说的导入时和运行时之间的区别。

在上面这个例子中,如果你不想在导入的时候就将函数append到registry列表中,而只想在被函数被显式调用才被记录到列表中时,可以将装饰器中的内容放到inner函数中,如下

def register(func):
	def inner():
		print('running register(%s)' % func)
		registry.append(func)
	return inner

运行结果为

running main()
registry -> []
running register(<function f1 at 0x00000221E7540288>)
running register(<function f2 at 0x00000221E75403A8>)
running f3()

装饰器的叠放

在函数f上,我们可以放置多个python装饰器,例如

@d1
@d2
def f():
	print('f')

其等价于

f = d1(d2(f))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值