前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解闭包与装饰器

深入理解闭包与装饰器

原创
作者头像
Heaven645
发布2024-08-14 21:36:39
830
发布2024-08-14 21:36:39
举报
文章被收录于专栏:Python学习

一、闭包

①定义

闭包是指一个函数(内层函数)能够“记住”并访问它所在作用域的变量(外层函数的变量),即使在外层函数已经返回的情况下。

②优缺点

优点:

  • 无需定义全局变量即可实现通过函数持续地访问、修改某个值
  • 闭包使用的变量的所用于在函数内,难以被错误的调用修改

缺点:

  • 内部函数会持续引用外部函数的值,导致这一部分内存无法释放,一直占用内存

③基本写法

代码语言:python
代码运行次数:0
复制
def outer_function(outer_var):  
    def inner_function(inner_var):  
        函数体
    return inner_function
  • outer_function:外部函数名称,负责接收一个参数并定义一个内部函数
  • outer_var:传递给 outer_function 的一个参数。
  • inner_function:在 outer_function 内部定义的函数
  • inner_var:传递给 inner_function 的参数

整体作用:

当调用outer_function时,它会返回一个新的函数inner_function,这个新的函数“记住”了outer_var的值,inner_function可以使用这个值与传入的inner_var进行操作。

④nonlocal关键字

在闭包函数(内部函数中)想要修改外部函数的变量值时,需要用nonlocal关键字声明这个外部变量。

代码语言:python
代码运行次数:0
复制
def outer(num1):
    def inner(num2):
        # 声明要使用外部函数 outer 的 num1
        nonlocal num1
        num1=+num2
        print(num1)
    return inner
fn=outer(10)
fn(10)
fn(10)
fn(10)

输出结果:

20

30

40

【分析】

调用 fn(10) 时,实际上是调用了 inner 函数。num1 的初始值为 10,每次调用 fn(10) 时,都会把 10 加到 num1 上,并输出更新后的值。由于 num1 是通过 nonlocal 声明的,所以每次调用都能“记住并修改 num1 的值。

【案例】

使用全局变量account_amount记录余额的简单ATM操作:

代码语言:python
代码运行次数:0
复制
# 通过全局变量account_amount记录余额
account_amount=0
def atm(num, deposit=True):
    global account_amount
    if deposit:
        account_amount += num
        print(f"存款:+{num}, 账户余额:{account_amount}")
    else:
        account_amount -= num
        print(f"取款:-{num}, 账户余额:{account_amount}")

atm(100)
atm(200)
atm(100, deposit=False)

运行结果:

存款:+100, 账户余额:100

存款:+200, 账户余额:300

取款:-100, 账户余额:200

【分析】

该实现简单明了,但存在全局变量带来的命名空间污染问题,容易出现变量被修改的隐患。

【改进】

通过闭包来管理ATM账户状态:

代码语言:python
代码运行次数:0
复制
# 使用闭包实现ATM小案例
def account_create(initial_amount=0):

    def atm(num, deposit=True):
        nonlocal initial_amount
        if deposit:
            initial_amount += num
            print(f"存款:+{num}, 账户余额:{initial_amount}")
        else:
            initial_amount -= num
            print(f"取款:-{num}, 账户余额:{initial_amount}")

    return atm

atm = account_create()

atm(100)
atm(200)
atm(100, deposit=False)

运行结果:

存款:+100, 账户余额:100

存款:+200, 账户余额:300

取款:-100, 账户余额:200

【分析】

通过使用闭包,这段代码实现了一个简单而有效的账户系统,封装了账户余额的管理,确保了数据的安全性和操作的简便性。

二、装饰器

①定义

装饰器是一个函数,它接受另一个函数作为参数,并返回一个新的函数。这个新的函数通常会在原函数的基础上添加一些额外的功能。

本质上,装饰器也是闭包,它可在不改变目标函数的基础上,为其增加额外功能,可以看作是在函数“外面”包裹了一层新的逻辑。

②写法

写法一:闭包

定义一个闭包函数, 在闭包函数内部执行目标函数并完成功能的添加。

代码语言:python
代码运行次数:0
复制
# 定义装饰器
def outer(func):
    def inner():
        print("我睡觉了")
        func()
        print("我起床了")
    return inner
    
# 被装饰的函数
def sleep():
    import random
    import time
    print("睡眠中……")
    time.sleep(random.randint(1,5))
    
# 应用装饰器
fn=outer(sleep)
# 调用增强后的函数
fn() # 调用 fn() 实际上是调用 inner 函数

输出结果:

我睡觉了

睡眠中……

我起床了

【分析】

通过使用闭包,装饰器的使用使得sleep函数的行为在不修改其内部实现的情况下得到了扩展,添加了额外的行为。

写法二:语法糖

使用“@”语法糖简化装饰器的定义:

代码语言:python
代码运行次数:0
复制
# 定义装饰器
def outer(func):
    def inner():
        print("我睡觉了")
        func()
        print("我起床了")

    return inner
    
# 使用装饰器的语法糖
@outer
def sleep():
    import random
    import time
    print("睡眠中……")
    time.sleep(random.randint(1,5))

# 调用装饰后的函数
sleep()

输出结果:

我睡觉了

睡眠中……

我起床了

【分析】

@outer是装饰器的语法糖,等价于sleep = outer(sleep)。其工作流程如下:

  • 使用 @outer 装饰 sleep 函数时,它会把 sleep 函数传递到 outer 函数中;
  • outer 返回一个 inner 函数,这个 inner 函数包含原来的 sleep 函数;
  • sleep 变量现在指向 inner 函数,所以以后的所有对 sleep() 的调用实际上都在执行 inner()。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、闭包
  • 二、装饰器
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档