day03.18

闭包函数

闭包函数的定义理解:

如果在一个函数的内部定义了另一个函数,外部的我们叫他外函数,内部的我们叫他内函数。在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

闭包函数的两大特征:

  • 闭:定义在函数内部的函数
  • 包:内部函数使用了外层函数名称空间中的名字
复制代码
 #闭包函数的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
    b = 10
    # inner是内函数
    def inner():
        #在内函数中 用到了外函数的临时变量
        print(a+b)
    # 外函数的返回值是内函数的引用
    return inner

if __name__ == '__main__':
    # 在这里我们调用外函数传入参数5
    #此时外函数两个临时变量 a是5 b是10 ,并创建了内函数,然后把内函数的引用返回存给了demo
    # 外函数结束的时候发现内部函数将会用到自己的临时变量,这两个临时变量就不会释放,会绑定给这个内部函数
    demo = outer(5)
    # 我们调用内部函数,看一看内部函数是不是能使用外部函数的临时变量
    # demo存了外函数的返回值,也就是inner函数的引用,这里相当于执行inner函数
    demo() # 15

    demo2 = outer(7)
    demo2()#17
复制代码

 

def outer():
    x = 999
    def inner():
        print('from outer>>>inner',x)
    return inner
x = 666
res = outer()    # 把outer的返回值赋予res
res()

 

闭包函数的实际应用:

闭包函数是给函数体传参的另一种形式

  • 函数体传参的方式:形参
def index(username):
    print(username)


# 函数体代码需要什么就可以在形参中写什么
index('jason')
  • 函数体传参的另一种方式:闭包
复制代码
def outer():
    username = 'jason'

    def index():
        print(username)  # 永远使用的都是jason

    return index


res = outer()
复制代码
复制代码
#修改闭包变量的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
    b = 10  # a和b都是闭包变量
    c = [a] #这里对应修改闭包变量的方法2
    # inner是内函数
    def inner():
        #内函数中想修改闭包变量
        # 方法1 nonlocal关键字声明
        nonlocal  b
        b+=1
        # 方法二,把闭包变量修改成可变数据类型 比如列表
        c[0] += 1
        print(c[0])
        print(b)
    # 外函数的返回值是内函数的引用
    return inner

if __name__ == '__main__':

    demo = outer(5)
    demo() # 6  11
复制代码

note:使用闭包的过程中,一旦外函数被调用一次返回了内函数的引用,虽然每次调用内函数,是开启一个函数执行过后消亡,但是闭包变量实际上只有一份,每次开启内函数都在使用同一份闭包变量!!!

装饰器

python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象!装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。

装饰器的本质:

在不改变被装饰对象原有的'调用方式'和'内部代码'的情况下,给被装饰对象添加新的功能。

装饰器的原则:对功能扩展开放、对代码修改封闭。

简易版本的装饰器

复制代码
import time


def index():
    time.sleep(1)
    print('from index')


def home():
    time.sleep(3)
    print('from home')


print(home)


def outer(func):  # 真正的index被outer局部名称空间存储了
    def get_time():
        start_time = time.time()  # 函数执行之前获取一个时间戳
        func()  # 调用了真正的index函数
        end_time = time.time()  # 函数执行之后获取一个时间戳
        print(end_time - start_time)  # 两个时间戳的差值就是函数的执行时间

    return get_time
复制代码

 

进阶版本的装饰器

# 解决的是参数问题
def outer(func_name):
    def get_time(*args, **kwargs): 
        start_time = time.time()
        func_name(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
    return get_time

 

完整版本的装饰器

复制代码
# 解决的是返回值问题
def outer(func_name):
    def get_time(*args, **kwargs):
        start_time = time.time()
        res = func_name(*args, **kwargs)  # 执行真正的index函数
        end_time = time.time()
        print(end_time - start_time)
        # return '不要急躁' # 如何在此处返回真正index函数的返回值
        return res
    return get_time
复制代码

 

装饰器的模板

编写装饰器其实有一套固定的代码。

def outer(func_name):  # func_name用于接收被装饰的对象(函数)
    def inner(*args, **kwargs):
        print('执行被装饰函数之前 可以做的额外操作')
        res = func_name(*args, **kwargs)  # 执行真正的被装饰函数
        print('执行被装饰函数之后 可以做的额外操作')
        return res  # 返回真正函数的返回值
    return inner

装饰器语法糖

仅仅是让代码编写的更加好看、简洁!!!

语法糖内部原理:

  1. 使用的时候最好紧跟在被装饰对象的上方
  2. 语法糖会自动将下面紧挨着的函数名传给@后面的函数调用
复制代码
def outer(func_name):
    def inner(*args, **kwargs):
        print('执行函数之前的操作')
        res = func_name(*args, **kwargs)
        # 额外操作
        return res
    return inner

@outer  # 等价于 index = outer(index)
def index(*args, **kwargs):
    print('from index')
# index = outer(index)  # 总感觉这一行代码有点low!!!

@outer  # 等价于 home = outer(home)
def home(*args,**kwargs):
    print('from home')


print(index)
print(home)
复制代码

装饰器的修复技术

用@wraps来修复装饰器装饰后的函数的名称等属性发生的变化。

复制代码
from functools import wraps
def outer(func_name):
    @wraps(func_name)
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的操作')
        res = func_name(*args, **kwargs)
        return res
    return inner
@outer
def index():
    print('from index')
@outer
def home():
    print('from home')
复制代码

 

posted @   *sunflower*  阅读(57)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示