Python学习笔记(二十二):装饰器


装饰器:

如果有一些函数,已经实现了某些功能,要求在不修改这些函数源代码,并且调用名称不变的前提下,对这些函数的功能进行一些扩展,就可以使用装饰器来实现;

比如存在如下两个函数:

# 定义两个函数
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()

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值