装饰器:
如果有一些函数,已经实现了某些功能,要求在不修改这些函数源代码,并且调用名称不变的前提下,对这些函数的功能进行一些扩展,就可以使用装饰器来实现;
比如存在如下两个函数:
# 定义两个函数
def test1():
print("=== test1 ===")
def test2():
print("=== test2 ===")
# 调用函数
test1()
test2()
要求不能修改原函数的代码,以及调用名称不变的情况下,对函数功能进行扩展:
# 定义一个通用函数,参数用于接收原函数的引用
def test(func):
# 定义一个闭包函数,在闭包中对原函数功能进行扩展
def inner():
print("=== 扩展的功能 ===")
func() # 调用原函数
# 返回闭包函数的引用
return inner
# 定义两个函数
def test1():
print("=== test1 ===")
def test2():
print("=== test2 ===")
# 调用通用函数,将原函数的引用传入,返回闭包函数的引用
test1 = test(test1)
# 通过闭包函数的引用调用闭包函数,在闭包函数中实现对
# 原函数的扩展,然后再调用原函数;
# 注意:此时的 test1() 不再是直接调用 test1 函数了,而是调用的闭包函数;
test1()
print("==========================")
test2 = test(test2)
test2()
输出结果:
装饰器原理的解释:
1、当 python 解析器执行到 test1 = test(test1) 代码的时候,调用通用函数 test(),然后将原函数 test1 的引用传入,用参数 func 接收;那么此时,test() 函数中的参数 func 也变成了 test1 原函数的引用;
2、 test() 函数执行结束,返回闭包函数的引用(return inner),用变量 test1 接收;那么此时,变量 test1 不再指向原函数 test1 了,而是指向闭包函数 inner() 了;
3、最后调用 test1() 的时候,调用的就是闭包函数 inner() 了;在闭包函数 inner() 中实现了扩展的功能,然后在调用 func() 函数;从 1 我们已经知道,此时变量 func 是原函数 test1 的引用;
上面介绍的是装饰器的原理,装饰器的功能代码 test1 = test(test1) 可以直接在 test() 函数头上加一个 @test 装饰器来实现:
# 定义一个通用函数,参数用于接收原函数的引用
def test(func):
# 定义一个闭包函数,在闭包中对原函数功能进行扩展
def innner():
print("=== 扩展的功能 ===")
func() # 调用原函数
# 返回闭包函数的引用
return innner
# 定义两个函数
# 装饰器 @test 就相当于 test1 = test(test1)
@test
def test1():
print("=== test1 ===")
@test
def test2():
print("=== test2 ===")
# 调用函数
test1()
test2()
多个装饰器的使用顺序:
# 定义一个装饰器函数:功能是对原函数的返回值进行加粗设置
def makeBold(fn):
# 定义一个闭包函数
def wrapped():
return "<b>" + fn() + "</b>"
# 返回闭包的引用
return wrapped
# 定义一个装饰器函数:功能是对原函数的返回值进行斜体设置
def makeItalic(fn):
# 定义一个闭包函数
def wrapped():
return "<i>" + fn() + "</i>"
# 返回闭包的引用
return wrapped
# 定义原函数,使用装饰器
@makeBold # 相当于 test = makeBold(test)
@makeItalic # 相当于 test = makeItalic(test)
def test():
return "hello world"
# 调用函数
print(test())
输出结果:
从结果可以看出,当使用多个装饰器的时候,是从下往上的顺序依次调用的;
装饰有参函数:
对定长参数的函数进行装饰:
# 定义一个装饰器函数
def test(fn):
# 闭包函数也要接受两个参数
def test_in(a_in, b_in):
print("=== 扩展的功能 ===")
# 调用原函数的时候把参数传入
fn(a_in, b_in)
return test_in
# 对有参函数进行装饰(原函数含有两个参数)
# @test 相当于 test1 = test(test1)
@test
def test1(a, b):
print("a = %d, b = %d" %(a, b))
# 调用函数:此时 test1 是装饰器中闭包函数的引用
test1(22, 33)
对不定长参数的函数进行装饰:
# 定义一个装饰器函数
def test(fn):
# 闭包函数:*args 表示以元组的方式接收不定长参数
def test_in(*args):
print("=== 扩展的功能 ===")
# 调用原函数的时候把参数传入
fn(*args)
return test_in
# 对有参函数进行装饰(原函数含有两个参数)
# @test 相当于 test1 = test(test1)
@test
def test1(a, b):
print("a = %d, b = %d" %(a, b))
@test
def test2(a, b, c, d):
print("a = %d, b = %d, c = %d, d = %d" %(a, b, c, d))
# 调用函数:此时 test1、test2 都是装饰器中闭包函数的引用
test1(22, 33)
test2(22, 33, 44, 55)
通用装饰器:
# 定义一个通用装饰器函数
def test(fn):
# 闭包函数:*args 表示以元组的方式接收不定长参数
def test_in(*args):
print("=== 扩展的功能 ===")
# 调用原函数的时候把参数传入,并返回原函数的返回值
return fn(*args)
# 返回闭包函数的引用
return test_in
# 对普通函数进行装饰
@test
def test1():
print("=== test1 ===")
# 对有返回值函数进行装饰
@test
def test2():
print("=== test2 ===")
return "hello world"
# 对有参函数进行装饰
@test
def test3(a, b, c):
print("=== test3 ===")
print("a = %d, b = %d, c = %d" %(a, b, c))
# 调用普通函数
test1()
print("****************************")
# 调用有返回值的函数
print(test2())
print("****************************")
# 调用有参函数
test3(11, 22, 33)
print("****************************")
带参数的装饰器:
需要在装饰器函数外面再套一个函数,专门用来接收装饰器的参数;
# 在装饰器外面再套一个函数,用于接收装饰器的参数
def test_arg(arg):
# 定义一个通用装饰器函数(此时的装饰器函数是一个闭包)
def test(fn):
# 闭包函数:*args 表示以元组的方式接收不定长参数
def test_in(*args):
print("装饰器的参数 arg:%s" %arg)
print("=== 记录日志 ===")
# 调用原函数的时候把参数传入,并返回原函数的返回值
return fn(*args)
# 返回闭包函数的引用
return test_in
# 返回装饰器函数的引用
return test
# @test_arg("hello"):先执行 test_arg("hello") 函数,返回装饰器函数 test 的引用,
# 此时装饰器就变成了 @test;然后就可以像普通的装饰器一样使用了;
@test_arg("hello")
def test1():
print("=== test1 ===")
# 调用函数
test1()