一. 装饰器函数的构成
假设业务部门需要对四个不同功能的函数,增加验证功能,如何对下列代码进行修改?
def foo1():
print("foo1")
def foo2():
print("foo2")
def foo3():
print("foo3")
def foo4():
print("foo4")
foo1()
foo2()
foo3()
foo4()
需要验证功能,可以写一个验证功能的函数,插入到每一个功能函数中
# -*- coding:utf-8 -*-
def check_foo():
print("check")
def foo1():
check_foo()
print("foo1")
def foo2():
check_foo()
print("foo2")
def foo3():
check_foo()
print("foo3")
def foo4():
check_foo()
print("foo4")
foo1()
foo2()
foo3()
foo4()
但是,需要了解的是,公司中,老板一般会要求:对代码的修改遵循开放封闭原则,开放-可对代码进行扩展,封闭-已经实现的代码不允许修改。
但是,每个功能代码中插入了自己的实现函数,已经进行了修改,没有满足老板的要求。
这时就需要使用python中的装饰器,它的组成类似于闭包。
扩展自己写的check_foo函数,封装成一个装饰器函数
def wrapper(foo):
def check_foo():
print("check")
foo()
return check_foo
随后使用@[函数名]在每一个功能函数上来调用装饰器函数
# -*- coding:utf-8 -*-
@wrapper
def foo1():
print("foo1")
@wrapper
def foo2():
print("foo2")
@wrapper
def foo3():
print("foo3")
@wrapper
def foo4():
print("foo4")
foo1()
foo2()
foo3()
foo4()
执行结果:
check
foo1
check
foo2
check
foo3
check
foo4
二. 装饰器函数的过程
- 在使用
@wrapper时,首先执行wrapper函数,且将@wrapper下的函数的引用作为参数传递给wrapper函数去执行 - 后续过程与闭包函数的执行过程一样
@wrapper可以理解为,等于wrapper(foo1)
三. 装饰器的深入使用
1. 装饰器嵌套执行
例子:
# -*- coding:utf-8 -*-
def font1(fn):
def wrapper():
return "<b>" + fn() + "</b>"
return wrapper
def font2(fn):
def wrapper():
return "<i>" + fn() + "</i>"
return wrapper
@font1
def words1():
return "today is a good day!"
@font2
def words2():
return "nothing can change my mind"
@font1
@font2
def words3():
return "don't worry, be happy!"
print(words1())
print(words2())
# 将装饰器font2的结果给装饰器font1,装饰器嵌套执行
print(words3())
结果:
<b>today is a good day!</b>
<i>nothing can change my mind</i>
<b><i>don't worry, be happy!</i></b>
从最靠近函数的装饰器先执行,将执行完成的结果封装成一个函数,该函数再向外执行一个装饰器,迭代操作,直到完结。
2. 被装饰的函数带参数
被装饰的函数的参数会传入到,内嵌函数中当参数使用
例子:
# -*- coding:utf-8 -*-
from time import ctime, sleep
def time_foo(foo):
def wrapped_func(a, b):
print("%s 被调用 %s" % (foo.__name__, ctime()))
print(a, b)
foo(a, b)
return wrapped_func
@time_foo
def fun(a, b):
print(a + b)
fun(1, 3)
sleep(2)
fun(5, 7)
结果:
fun 被调用 Mon Dec 23 16:38:58 2019
1 3
4
fun 被调用 Mon Dec 23 16:39:00 2019
5 7
12
3. 被装饰函数的return返回值
接上面的例子,去掉被装饰函数以及装饰器内的参数,再在程序末尾加上一个speak函数,且不同于fun函数,函数内部是加上return返回值而不是print,但最后的结果不显示其返回值。
例子:
# -*- coding:utf-8 -*-
from time import ctime, sleep
def time_foo(foo):
def wrapped_func():
print("%s 被调用 %s" % (foo.__name__, ctime()))
foo()
return wrapped_func
@time_foo
def fun():
print("hello")
@time_foo
def speak():
return "prefect!"
fun()
speak()
print(speak())
结果:
fun 被调用 Mon Dec 23 16:55:32 2019
hello
speak 被调用 Mon Dec 23 16:55:32 2019
speak 被调用 Mon Dec 23 16:55:32 2019
None
原因是,此处的speak()由于被装饰器装饰,其返回值为装饰器内部嵌套函数wrapped_func的返回值,由于内部嵌套函数无返回值,所以打印为空None。
所以,要打印出被嵌套函数返回值,即,使装饰器内嵌套函数返回值变为被嵌套函数返回值。
即,修改
foo()为return foo()
4. 装饰器带参数
规律:从内到外,第一层函数,参数为被修饰函数参数;第二层函数,参数为被修饰函数的引用;第三层函数,参数为装饰器参数
# -*- coding:utf-8 -*-
from time import ctime
def time_foo_param(fal="prefect"):
def time_inner(foo):
def time_wrapper():
print("%s called at %s %s" % (foo.__name__, ctime(), fal))
return foo()
return time_wrapper
return time_inner
@time_foo_param()
def func():
print('Hello Python!')
func()
这里foo() == time_foo_param()(foo)()
至于前面的双层嵌套函数,规律同上面总结的一样,只不过没有第三层函数,所以无法为装饰器添加参数,但前两层所具备的功能都包含。
1502

被折叠的 条评论
为什么被折叠?



