【Python】闭包和装饰器

闭包

1. 闭包的介绍

闭包的定义:

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包

2. 闭包的构成条件

通过闭包的定义,我们可以得知闭包的形成条件:

  1. 在函数嵌套(函数里面再定义函数)的前提下
  2. 内部函数使用了外部函数的变量(还包括外部函数的参数)
  3. 外部函数返回了内部函数

3. 简单闭包的示例代码

# 定义一个外部函数
def func_out(num1):
    # 定义一个内部函数
    def func_inner(num2):
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)
    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner

# 创建闭包实例    
f = func_out(1)
# 执行闭包
f(2)
f(3)

运行结果:

结果是: 3
结果是: 4

闭包执行结果的说明:

通过上面的输出结果可以看出闭包保存了外部函数内的变量num1,每次执行闭包都是在num1 = 1 基础上进行计算。

4. 闭包的作用

  • 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。

注意点:

  • 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。

5. 修改闭包内使用的外部变量


# 定义一个外部函数
def func_out(num1):

    # 定义一个内部函数
    def func_inner(num2):
        nonlocal num1  # 告诉解释器,此处使用的是 外部变量a
        # 修改外部变量num1
        num1 = 10
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)

    print(num1)
    func_inner(1)
    print(num1)

    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner

# 创建闭包实例
f = func_out(1)
# 执行闭包
f(2)

 装饰器

1. 装饰器的定义

就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数

装饰器的功能特点:

  1. 不修改已有函数的源代码
  2. 不修改已有函数的调用方式
  3. 给已有函数增加额外的功能

2. 装饰器的示例代码

# 添加一个登录验证的功能
def check(fn):
    def inner():
        print("请先登录....")
        fn()
    return inner


def comment():
    print("发表评论")

# 使用装饰器来装饰函数
comment = check(comment)
comment()

# 装饰器的基本雏形
# def decorator(fn): # fn:目标函数.
#     def inner():
#         '''执行函数之前'''
#         fn() # 执行被装饰的函数
#         '''执行函数之后'''
#     return inner

代码说明:

  • 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器。
  • 写代码要遵循开放封闭原则,它规定已经实现的功能代码不允许被修改,但可以被扩展。

执行结果:

请先登录....
发表评论

3. 装饰器的语法糖写法

如果有多个函数都需要添加登录验证的功能,每次都需要编写func = check(func)这样代码对已有函数进行装饰,这种做法还是比较麻烦。

Python给提供了一个装饰函数更加简单的写法,书写格式是: @装饰器名字,通过语法糖的方式也可以完成对已有函数的装饰

# 添加一个登录验证的功能
def check(fn):
    print("装饰器函数执行了")
    def inner():
        print("请先登录....")
        fn()
    return inner

# 使用语法糖方式来装饰函数
@check
def comment():
    print("发表评论")


comment()

说明:

  • @check 等价于 comment = check(comment)
  • 装饰器的执行时间是加载模块时立即执行。

执行结果:

请先登录....
发表评论

4. 装饰器实现已有函数执行时间的统计

import time

# 装饰器函数
def get_time(func):
    def inner():
        begin = time.time()
        func()
        end = time.time()
        print("函数执行花费%f" % (end-begin))
    return inner


@get_time
def func1():
    for i in range(100000):
        print(i)


func1()

执行结果:


...
99995
99996
99997
99998
99999
函数执行花费0.329066

5. 通用装饰器

# 添加输出日志的功能
def logging(fn):
    def inner(*args, **kwargs):
        print("--正在努力计算--")
        result = fn(*args, **kwargs)
        return result

    return inner


# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
    result = 0
    for value in args:
        result += value

    for value in kwargs.values():
        result += value

    return result


@logging
def subtraction(a, b):
    result = a - b
    print(result)

result = sum_num(1, 2, a=10)
print(result)

subtraction(4, 2)

运行结果:

--正在努力计算--
13
--正在努力计算--
2

6. 多个装饰器的使用示例代码

 
def wrapper1(f1):
    print('in wrapper1')
    def inner1(*args,**kwargs):
        print('in inner1')
        ret = f1(*args,**kwargs)
        return ret
    return inner1
 
def wrapper2(f2):
    print('in wrapper2')
    def inner2(*args,**kwargs):
        print('in inner2')
        ret = f2(*args,**kwargs)
        return ret
    return inner2
 
def wrapper3(f3):
    print('in wrapper3')
    def inner3(*args,**kwargs):
        print('in inner3')
        ret = f3(*args,**kwargs)
        return ret
    return inner3
 
@wrapper1
@wrapper2
@wrapper3
def func():
    print('in func')
func()
# in wrapper3
# in wrapper2
# in wrapper1
# in inner1
# in inner2
# in inner3
# in func
#装饰器函数在被装饰函数定义好后立即执行。多个装饰器的调用顺序是自下往上的。
# 被装饰函数执行时,装饰器的执行顺序是从上往下的。

分析
先执行@wrapper3  等于 func = wrapper3(func) ---> 先打印'in wrapper3', 返回 inner3(把这个作为参数传给wrapper2) 【f3 = func】
在执行@wrapper2  等于执行  wrapper2(inner3) ---> 先打印'in wrapper2', 返回 inner2(把这个作为参数传给wrapper1)  【f2 = inner3】
在执行@wrapper1  等于执行  wrapper1(inner2) ---> 先打印'in wrapper2', 返回 inner1
此时func = inner1 【f1 = inner2】
运行func() 等价于执行inner1()  ---> 先打印'in inner1'  再执行f1() 【装饰器中wrapper1(f1),实际 wrapper1(inner2),所以f1=inner2  可以理解f1是形参,inner2是实参】 
再运行inner2()  ---> 先打印'in inner2' 再执行f2()  f2=inner3
再运行inner3()  --->先打印'in inner3' 再执行f3()     f3=func
再运行func()  --->先打印'in func'

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
闭包是一个函数对象,它引用了一些在其定义时外部环境的变量。在Python中,闭包常常用来创建装饰器装饰器其实就是一个闭包,它接受一个函数作为参数,并返回一个替代版函数。闭包允许我们在不修改原始函数的情况下,添加一些额外的功能或逻辑。 一个典型的装饰器的例子可以是这样的:在一个函数外面定义一个装饰器函数,然后通过在要装饰的函数之前添加@装饰器名称的语法糖,来应用装饰器闭包装饰器的实现机制是类似的,都是通过嵌套函数的方式来实现的。在闭包中,内部函数引用了外部函数的变量。而在装饰器中,装饰器函数接受一个函数作为参数,并返回一个内部函数,内部函数可以访问外部函数的变量。 在闭包装饰器的实现过程中,都需要注意作用域的规则。在闭包中,内部函数可以访问外部函数的局部变量,而在装饰器中,装饰器函数可以访问被装饰函数的变量。 闭包装饰器提供了一种灵活的方式来扩展函数的功能,使得我们可以在不修改原始函数的情况下,添加一些额外的逻辑。它们是Python中非常强大而且常用的特性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [python中的闭包装饰器解释](https://blog.csdn.net/qq_39567748/article/details/99596644)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [python高级之装饰器](https://blog.csdn.net/qq_35396496/article/details/109147229)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值