32.闭包、装饰器

闭包

定义双层嵌套函数,内层函数可以访问外层函数的变量,将内层函数作为外层函数的返回,此内层函数就是闭包函数

通过全局变量来定义变量时,尽管功能实现是OK的。但是仍有问题:

  • 代码在命名空间上(变量定义)不够干净、整洁
  • 全局变量有被修改的风险

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

闭包注意事项

优点,使用闭包可以让我们得到:

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

缺点:

  • 由于内部函数持续引用函数的值,所以会导致这一部分内存空间不被释放,一直占用内存
# 简单的闭包
def outer(logo):
    def inner(msg):
        print(f"<{logo}>{msg}<{logo}>")
    return inner

fn1 = outer("黑马程序员")
fn1("大家好")

fn2 = outer("传智教育")
fn2("学Python")
<黑马程序员>大家好<黑马程序员>
<传智教育>学Python<传智教育>

修改外部函数变量的值

需要使用nonlocal关键字修饰外部函数的变量才可在内部函数中修改它

# 简单的闭包
def outer(num1):
    def inner(num2):
        nonlocal num1
        num1 += num2
        print(num1)
    return inner

fn = outer(10)
fn(10)
fn(10)
fn(10)
20
30
40
# 使用闭包实现ATM小案例
def account_create(initial_account=0):
    def atm(num, deposit=True):
        nonlocal initial_account
        if deposit:
            initial_account += num
            print(f"存款:+{num}, 账户余额:{initial_account}")
        else:
            initial_account -= num
            print(f"存款:-{num}, 账户余额:{initial_account}")
    return atm

atm = account_create()
atm(100)
atm(200)
atm(100)
atm(200, False)
存款:+100, 账户余额:100
存款:+200, 账户余额:300
存款:+100, 账户余额:400
存款:-200, 账户余额:200

装饰器

装饰器其实也是一种闭包,其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能

def sleep():

  import random

  import time

  print("睡眠中.......")

  time.sleep(random.randint(1, 5))

希望给sleep函数,增加一个功能:

  • 在调用sleep前输出:我要睡觉了
  • 在调用sleep后输出:我起床了
# 装饰器的一般写法(闭包写法)
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()
我要睡觉了
睡眠中.......
我起床了
# 装饰器的语法糖写法,在不改变sleep函数的前提下,增加了inner函数内部的功能
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()
我要睡觉了
睡眠中.......
我起床了