python中的闭包和装饰器

本文深入探讨Python中的装饰器概念,从闭包出发,逐步解析装饰器的原理与应用。通过实例展示装饰器如何优化函数调用流程,以及如何处理函数参数和返回值。同时,介绍装饰器带参数的使用方式,以及如何利用functools.wraps保持被装饰函数的元信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

python中的装饰器可以说是一种高级语法了,是对闭包的一种封装。所谓闭包,用通俗的话来讲,定义一个函数,在函数内部再定义一个函数,内部定义的函数使用了外部函数的变量,同时返回值是内部函数。

闭包

闭包示例:

def test(num):
    def test_in(x):
        y = x + num
        print(y)
    return test_in


if __name__ == '__main__':
    ret = test(1)
    print(ret)

内部函数对外部函数变量(非全局变量)的引用,则称内部函数为闭包。闭包优化了变量,但是由于闭包引用了外部函数的局部变量,外部函数的局部变量没有及时释放,消耗内存。

假设这样一个业务场景,f1函数需要验证才能调用,为了遵循开放封闭原则,不能对f1函数进行改动,比较好的一个方法就是另外写一个验证函数,这个验证函数传入的参数就是所要调用的函数名。

def authority(func):
    def inner():
        print('正在进行验证')
        # 此处可加验证函数,这里略过不写
        func()
    return inner

def f1():
    print('----1----')



if __name__ == '__main__':
    f1 = authority(f1) #传入所要使用的函数名
    f1()

结果

正在进行验证
----1----

装饰器

def authority(func):
    def inner():
        print('正在进行验证')
        func()
    return inner

@authority #装饰器
def f1():
    print('----1----')


if __name__ == '__main__':
    f1()

结果

正在进行验证
----1----

可以发现,闭包和装饰器达到的效果是一样的,装饰器是闭包的一种封装效果,称为‘’语法糖‘’。

装饰器的执行顺序以及被装饰的函数带有传参

下面的示例是将hello world装饰成<html><head>hello world</head></html>,对一个函数运用了两次装饰器。

def head(func):
    print('正在进行head装饰')
    def inner1(args): #这里用来接收函数的传参
        new_args = '<head>' + args + '</head>'
        func(new_args)
    return inner1

def html(func):
    print('正在进行html装饰')
    def inner2(args):
        new_args = '<html>' + args + '</html>'
        func(new_args)
    return inner2

@head
@html
def hello(body):
    print(body)


if __name__ == '__main__':
    hello('hello world')

结果

正在进行html装饰
正在进行head装饰
<html><head>hello world</head></html>

总的来说,两个装饰器在执行时,是先执行后装饰的函数,即先装饰html,后装饰head,但是print的顺序是正序的。

装饰函数的返回值

def good_output(fun_name):
    def wrapper(*args, **kwargs): #这里接收所调用函数的传参
        ret = fun_name(args[0], args[1]) # 函数的返回值
        new_ret = '%s + %s = %s'%(args[0], args[1], ret)
        return new_ret
    return wrapper

@good_output
def input_num(a, b):
    return a+b


if __name__ == '__main__':
    result = input_num(1, 2)
    print(result)

结果

1 + 2 = 3

装饰器带有传参

def print_num(num):
    def func(fun_name):
        def wrapper():
            if 'two' == num:
                fun_name()
                fun_name()
            if 'one' == num:
                fun_name()
        return wrapper
    return func

@print_num('one')
def hello_world():
    print(hello_world.__name__)

@print_num('two')
def hello_china():
    print(hello_china.__name__)


if __name__ == '__main__':
    hello_world()
    hello_china()

结果:

wrapper
wrapper
wrapper

装饰器的传参可以有不同的用处,这里的示例仅作为一个打印数量的选择,但是有一个问题,hello_world.__name__打印的应该是函数的名称,由于这里使用了闭包,函数的名称变成了wrapper,这个问题可以用python自带的库解决。

functools.wraps

from functools import wraps
def print_num(num):
    def func(fun_name):
        @wraps(fun_name) #保持装饰的函数的 __name__ 的值不变
        def wrapper():
            if 'two' == num:
                fun_name()
                fun_name()
            if 'one' == num:
                fun_name()
        return wrapper
    return func

@print_num('one')
def hello_world():
    print(hello_world.__name__)

@print_num('two')
def hello_china():
    print(hello_china.__name__)


if __name__ == '__main__':
    hello_world()
    hello_china()

结果

hello_world
hello_china
hello_china
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值