Py进阶|装饰器
函数装饰器是函数替换的过程——被装饰的函数被替换成另一个东西。
# 带一个参数的函数,可作为装饰器函数
def foo (x):
print('foo')
@foo
#使用 @foo装饰 bar函数,bar函数是被装饰的函数
def bar ():
print('bar')
print('------------')
print(bar)
foo
------------
None
以上涉及到两个函数:
- 装饰器函数,用于修饰其他函数的东西,用
@
标识。理论上来说,任意一个带一个形参的函数都可以作为装饰器函数。 - 被装饰函数,被装饰器修饰的函数。
上面的程序中,使用foo()函数装饰bar()函数。
我们来分析一下输出结果
foo
------------
None
foo
说明foo()函数被调用了,而且在print('------------')
前
print(bar)
的结果是None,难道是bar()函数变成了None?
其实是被装饰的函数bar()
被替换成了另一个东西,即被替换成装饰器foo()
函数的函数返回值。这个过程大致是:被装饰函数bar()
作为参数传入装饰器函数foo()
,然后装饰器函数foo()
在内部使用这个被传递进来的函数bar()
,最后改变了被装饰函数bar()
的返回结果。
由于上面的程序中foo()
函数没有返回值,相当于返回值是None,因此被装饰的函数就被替换成了None,因此程序调用print(bar)就看到输出None了。
# 带一个参数的函数,可作为装饰器函数
def foo (x):
print('foo')
return '万事胜意'
@foo
#使用@foo装饰bar函数,bar函数是被装饰的函数
def bar ():
print('bar')
print('------------')
print(bar)
foo
------------
万事胜意
接着了解下装饰器函数中的参数。装饰器函数需要带一个形参。
# 带一个参数的函数,可作为装饰器函数
def foo (x):
print('foo')
print(x)
@foo
#使用@foo装饰bar函数,bar函数是被装饰的函数
def bar ():
print('bar')
foo
<function bar at 0x00000222BFE0E7B8>
上面的程序里,我们只是定义了两个函数,并没有写调用语句,但依旧有结果输出。背后的原因是每次用”@装饰器函数“去装饰其他函数时,装饰器函数就会被调用。
形参由谁传入值?
答:Python会自动将被装饰的函数作为参数传入装饰器函数。因此上面程序中装饰器函数foo()中第二行输出x参数,输出的就是被装饰的bar()函数。
装饰器函数的参数能不能是多个?
答:每次被装饰的函数只有一个,因此必须有一个、且只要一个参数来接收被装饰的函数。
前面的例子有些过于简单,日常也不会是返回None。我们希望函数被装饰后返回的还是函数,只要让装饰器函数返回函数即可。
# 带一个参数的函数,可作为装饰器函数
def foo (x):
print('foo')
return lambda y : print('嘻嘻嘻,', y)
@foo
#使用@foo装饰bar函数,bar函数是被装饰的函数
def bar ():
print('bar')
print('------------')
print(bar)
bar('哈哈哈')
foo
------------
<function foo.<locals>.<lambda> at 0x00000222BFE0EB70>
嘻嘻嘻, 哈哈哈
装饰器函数foo()函数返回了一个lambda表达式,相当于一个函数。按照装饰器的原理,被装饰的bar函数被替换成该lambda表达式,此时bar()函数就可以被调用了。
bar()函数被替换成了foo()函数返回的lambda表达式,此时bar()函数被彻底替换了。因此bar('哈哈哈')
表面上是调用bar函数,其实是调用foo()函数所返回的lambda表达式。
最终的效果是bar()函数时并没有定义形参,但程序调用bar()函数(表面上是调用bar()函数,实际上是调用foo()函数返回的lambda表达式)却可以传入一个参数(foo()函数返回的lambda表达式定义了一个参数)。
公众号.png