闭包和装饰器

闭包

Python函数是支持嵌套的。如果在一个内部函数中对外部函数作用域(非全部作用域)的变量进行引用,那么内部函数就会成为闭包。

闭包需要满足如下三个条件:
(1)存在于嵌套关系的函数中,并且闭包是内部函数。
(2)嵌套的内部函数引用了外部函数的变量(自由变量)。
(3)嵌套的外部函数会将内部函数名作为返回值返回。

接下来,通过一个案例演示如下:

# 外部函数
def outer(start=0):
    count = [start]  # 函数内的变量
    # 内部函数
    def inner():
        count[0] += 1   # 引用外部函数的变量
        return count[0]
    # 返回内部函数的名称
    return inner


qouter = outer(5)
print(qouter())

运行结果如下:

小脆筒

装饰器

装饰器本质是一个Python函数,它可以在不动其他函数的前提下,对函数的功能进行扩充。通常情况下,装饰器用于下列场景:
(1)引入日志;
(2)函数执行时间统计;
(3)执行函数前预备处理;
(4)执行函数后清理功能;
(5)权限校验;
(6)缓存。

先看一个简单的例子,希望可以输出函数的执行日志,代码如下:

def test_one():
    print('test_one')
    print('rest_one is running')


test_one()

但是,如果函数test_two()、test_three()都有类似的需求,那么现在这样的做法会出现大量重复的代码。为了减少重复的代码,我门可以创建一个新的函数专门记录函数执行日志,谁需要记录执行日志,就把谁作为参数传递,示例代码如下:

# 案例1
def print_log(func):
    print('函数正在运行中')
    func()


def test():
    print('test')


print_log(test)

按照上述代码将函数作为参数传递,虽然可以实现功能,但是却破坏了原代码的逻辑结构。如果要求已经实现的函数,不能修改,只能拓展,即遵守“封闭开放”原则,那么是不允许在函数test内部进行修改的。

装饰器可以满足上述需求。在Python中,装饰器语法是以 @ 开头,下面,我们写一个简单的装饰器。
代码如下:

def wrap(func):
    print('正在装饰')
    
    def inner():
        print('正在验证权限')
        func()
    return inner


@wrap     # @wrap等价于 test = wrap(test)
def test():
    print('test')

    
test()

运行结果如下:

小脆筒

多个装饰器

多个装饰器可以应用在一个函数上,它们的调用顺序是自上而下的。

用案例演示,代码如下:

def wrap_one(func):
    print('--正在装饰1--')

    def inner():
        print('--正在验证权限1--')
        # func()
    return inner


def wrap_two(func):
    print('--正在装饰2--')

    def inner():
        print('--正在验证权限2--')
        func()
    return inner()


@wrap_one
@wrap_two
def test():
    print('---test---')


# 调用test,调用test之前,已经装饰过
test()

上述代码中,test()函数上面有两个装饰器,按照多个装饰器从下往上的调用顺序,程序首先会执行 @wrap_two,在执行 @wrap_one。

运行结果如下:
x小脆筒

装饰器对有参数函数进行装饰

前面介绍的装饰器,都是对无参数的函数进行装饰,如果要对有参数的函数进行装饰,那么参数怎么设置呢?

用下列代码演示:

def wrap(func):
    def inner(a, b):
        print('开始验证权限')
        func(a, b)
    return inner


@wrap
def test(a, b):
    print('a = %d, b = %d' % (a, b))


test(1, 2)

在调用func()函数时,将 a,b参数传入,运行结果如下:

小脆筒

上面学习的装饰器只是对两个参数的含食宿使用,如果无法确定函数的参数以及参数类型,我们可以使用不定长参数来传递,示例代码如下:

# 用不定长参数传递
def wrap(func):
    def inner(*args, **kwargs):
        print('开始验证权限')
        func(*args, **kwargs)
    return inner


@wrap
def test(*args, **kwargs):
    print('-----test----')

test()
test(1, 2, 3)
test(a=1, b=2, c=3)

调用不同参数的函数,发现装饰器适用于不同参数的函数,运行结果如下:

小脆筒

带有参数的装饰器

前面我们所讲的装饰器,都是不带参数的,这些装饰器最终返回的都是函数名。如果我们给装饰器添加参数,那么需要添加一层封装,先传递参数,然后再传递函数名。

下面看一段代码:

# 带有参数的装饰器
def func_arg(args):
    def func(function_name):
        def func_in():
            print('--记录日记-args=%s' % args)
            function_name()
        return func_in()
    return func


@func_arg('haha')
def test():
    print('---test---')
    

运行结果如下:
小脆筒

END
感谢看到这里的各位读者朋友们,如果你感到本文写的不错,就顺手点个赞收藏一下,也可以关注一波~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小脆筒style

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值