python 装饰器

python这些奇奇怪怪的概念确实挺多了,不过好在终于快了解完了。闲话不多说,本问讲一下我对装饰器的理解,看过一遍装饰器之后,有点雾里看花的感觉,虽然可以强记下来,不过理解其中精要才是最该做的。

一,为什么需要装饰器?

当一个函数已经上线工作之后,虽然它本身没有问题,但是当需要添加功能的时候就比较麻烦了。首先要清楚,既然该函数已经可以在工作,那么再去修改这个函数的源代码就不合理也不现实了。所以就需要一种非入侵的方式,去在现有函数的基础上添加新功能。装饰器就是干这个事儿的。

那么,就来明确一下装饰器的目标。

  1. 不改变原有函数的源代码
  2. 不改变原有函数的调用方式,就是说如果原来在程序中调用test(1,2,3),那么添加新功能之后,依然用test(1,2,3)调用

二,怎么去实现装饰器?

首先我们不考虑python提供的装饰器,如何比改变原有函数的源代码,去添加功能?其实很简单,把函数作为参数传递给另一个高阶函数封装一下就可以了。

def work(i):
    print('工作函数执行... 参数(%d)' % i )

def log(func, i):
    print('%s 开始执行' % func.__name__)
    func(i)
    print('%s 结束执行' % func.__name__)

log(work, 8)

Output:
work 开始执行
工作函数执行... 参数(8)
work 结束执行

但是问题也很明显,不满足第二个要求,之前调用work(i) 现在得调用 log(work, i)。那么解决办法就是,把原来函数变成经过封装的函数。

def work(i):
    print('工作函数执行... 参数(%d)' % i )

def log(func):
    def wapper(*args, **kw):
        print('%s 开始执行' % func.__name__)
        func(*args, **kw)
        print('%s 结束执行' % func.__name__)

    return wapper

work = log(work) #work指向了经过我们封装的函数
work(8)

Output:
work 开始执行
工作函数执行... 参数(8)
work 结束执行

到这里,原来怎么调用work 现在依然怎么调用work,不过需要在前面加上一句 work = log(work)。

题外话:如果看不懂log函数是如何实现的,首先要理解python中的函数也是对象,这个指代函数的符号我们完全可以当作变量来对待,去当作参数传递,去作为返回值,去修改它指向新的函数。然后可以看一看 python函数参数的传递,这里这样写是可以接收任何参数的。

可以参考https://blog.csdn.net/qq_21294095/article/details/85074646

三,python提供的装饰器

python提供了@来简化装饰器书写,还是先看代码

def log(func):
    def wapper(*args, **kw):
        print('%s 开始执行' % func.__name__)
        func(*args, **kw)
        print('%s 结束执行' % func.__name__)

    return wapper

@log
def work(i):
    print('工作函数执行... 参数(%d)' % i )

work(8)
print(work.__name__)

Output:
work 开始执行
工作函数执行... 参数(8)
work 结束执行
wapper

只需要在需要装饰的函数前@log 就完全等价于 我们的 work = log(work)。

这里我故意在最后输出了work.__name__ 可以看到 经过装饰后work函数的__name__属性已经改变了,变成了wapper。我们需要让它保持不变。python依然提供了一个函数去做这个事儿。当然你如果像手动做估计也没人拦得住

import functools

def log(func):
    @functools.wraps(func)    #添加这个装饰器 那么func函数的属性就不会被修改
    def wapper(*args, **kw):
        print('%s 开始执行' % func.__name__)
        func(*args, **kw)
        print('%s 结束执行' % func.__name__)

    return wapper

@log
def work(i):
    print('工作函数执行... 参数(%d)' % i )

work(8)
print(work.__name__)

Output:
work 开始执行
工作函数执行... 参数(8)
work 结束执行
work

四,如果新功能需要参数该怎么办?

如果完全理解了前面的内容,那么使用闭包可以实现传参,把不含参数的装饰器作为内层函数,那么它就可以引用外层的参数。

关于python闭包的概念可以参考https://blog.csdn.net/qq_21294095/article/details/85093573

from functools import wraps

def log(msg):
    def deco(func):
        @wraps(func)
        def wapper(*args, **kw):
            print('新功能的参数 %s'% msg)
            print('%s 开始执行' % func.__name__)
            func(*args, **kw)
            print('%s 结束执行' % func.__name__)

        return wapper

    return deco

@log('添加的新功能所需的参数')
def work(i):
    print('工作函数执行... 参数(%d)' % i )

work(8)

Output:
新功能的参数 添加的新功能所需的参数
work 开始执行
工作函数执行... 参数(8)
work 结束执行

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值