如何理解Python装饰器

在Python中,当需要对某个函数的功能进行增强,又不修改其代码,而是在运行期间动态为其添加功能的方式,便是装饰器。

本质上,装饰器其实就是一个返回函数的高阶函数,所以在学习装饰器之前要理解Python中一切皆为对象的思想。

def print_log(func):
    def wrapper(*args, **kw):
        print(f'call {func.__name__}')
        return func(*args, **kw)
    return wrapper

@print_log
def greet():
   print('hello!')

以上就是一个最简单的用于打印日志的装饰器,把@pring_log放到greet方法定义语句上,实际等于:

greet = print_log(greet)

在执行时,动态为greet函数增加了功能。可以看到,通过把greet函数作为对象传给装饰器print_log后,greet函数作为一个对象此时实际等于wrapper对象。当我们在调用greet()时,其实执行的是print_log(greet)(),也就是装饰器中retrun的新函数wrapper()。(这里如果没有明白,要仔细理解函数也是对象这句话)

wrapper函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用

如果装饰器本身需要参数传入,那么我们要编写一个返回装饰器的高阶函数,会更复杂一些。假设我们要编写一个可以自定义日志输出语言的装饰器,那需要如下改动:

def print_content(content):
    def decorator(func):
        def wrapper(*args, **kw):
            print(f"this is personal content : {content}")
            print(f'call {func.__name__}')
            return func(*args, **kw)
        return wrapper
    return decorator
    
@print_content("how are you?")
def greet():
    print("hello!")

同样的,在方法定义加上@print_content等于:

greet = print_content("how are you?")(greet)

先执行print_content("how are you?")返回decorator对象,再将greet函数作为对象传给decorator,然后返回wrapper对象。只是多嵌套一层用于接收参数而已,本质上与之前的代码并没有不同。

经过上面一番操作后,函数的功能已经没有问题了。但是前文已经说过,函数也是对象,greet对象经过装饰器修改后,实际上已经是wrapper对象,我们在调用greet()时,用的也是wrapper()。此时的一些函数的内建属性,如__name__属性已经由greet变成了wrapper。通过内置的functools.wraps,即可轻松的解决这个问题:

import functools

def print_log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print(f'call {func.__name__}')
        return func(*args, **kw)
    return wrapper

文章参考:
装饰器-廖雪峰:https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值