闭包函数与装饰器

昨日内容回顾

  • 函数的参数

    位置参数 分为位置形参和位置实参,位置形参与实参数量要一致,使用关键字传参可改变赋值顺序。

    默认参数 函数定义阶段赋有默认值的参数,传参时可不传,不传即使用默认值。

    可变长参数 可变长形参:*args接收多余的位置实参,组织成元组赋值给星号后变量名。

    ​ **kwargs接收多余的关键字传参,组织成字典赋值给星号后变量名。

    ​ 可变长实参:*+可循环对象,将对象内的数据逐一取出一次性赋值给形参。

    ​ **+字典,将字典键值对打散按照关键字传参形式依次赋值给形参。

  • 名称空间

    名称空间是内存中用来存储名称与数值绑定关系的空间。

    名称空间的分类:内置名称空间、全局名称空间、局部名称空间。

    名称空间的存活时间及作用范围:

    ​ 内置名称空间:打开解释器产生,关闭销毁,全局生效。

    ​ 全局名称空间:打开程序文档产生,关闭销毁,文档全局生效。

    ​ 局部名称空间:运行函数代码产生,运行结束销毁,函数代码内生效。

    名称默认查找层级由上到下:内置名称空间>>>全局名称空间>>>局部名称空间>>>局部内嵌套的空间,在本层空间查找不到时到上层查找。

今日内容概要

  • global与nonlocal关键字
  • 函数名的使用方式
  • 闭包函数
  • 装饰器简介
  • 装饰器的推导流程
  • 装饰器模板
  • 装饰器语法糖

今日内容详细

global与nonlocal关键字

global+变量名 声明该处使用的变量名位于全局名称空间内。

nonlocal+变量名 函数嵌套情况下,内部函数使用,声明此处使用变量名位于外部函数名称空间内。

闭包函数

在函数嵌套时,内部的函数使用了外部函数中的变量名时,此时该外部函数即为闭包函数。

使用闭包函数可以做到在不改变内部函数组成的情况下为内部函数传参数。

装饰器简介

装饰器本质是一个函数,该函数的作用是,在不改变原函数组成及调用方式的情况下为原函数添加功能。可以理解为,装上装饰器后,实现的是扩展后的功能,去掉装饰器后,实现的还是原来的功能。

装饰器推导流程

  1. 函数是不可变数据类型,在一次程序运行中,函数一旦定义完成,便不支持修改,因此在使用装饰器后原函数的函数名与一个新的函数体绑定,新的函数体包含与原函数体完全相同的片段,即可满足要求;

  2. 函数名本身类似于变量名,因此将新函数的函数名赋值给原函数的函数名(此过程相当于链式赋值),实现新函数体代码绑定给原函数名,假设原函数名为 func_0,新函数名为func_1,则需要做的操作为 :

    func_0 = func_1;

  3. 经历此操作后,显然已经满足要求,但新函数不具有普遍适用性,要为新的函数装饰相同的功能时,依然需要通过修改函数体代码的方式实现,可见函数体的替换不是完美的解决方式;

  4. 通过新函数调用原函数的方式,且该新函数在调用时传入原函数的函数名,那么装饰功能对应代码就不用修改,就可以将装饰功能分离出来,因此我们需要考虑的就是构造这样一个新的装饰器函数;

  5. 假设该装饰器函数为decorator(),则装饰原函数的语句应为:func_0 = decorator(func_0),

    该语句的含义为将装饰器函数的返回值赋值给原函数名,此返回值应为装饰后的函数的函数名(此处参照步骤2),假设装饰后的函数的函数名为func_2,则decorator()的基本组成为:

    def decorator(func_name):
        def func_2():
            ...
    	return func_2
    
  6. 接下来需要考虑的就是func_2()的内容,装饰后的函数应该与原函数拥有相同的返回值,则对上述组成进一步完善,得到decorator()的组成为:

    def decorator(func_name):
        def func_2():
            """Here may be a picec of code to decorate func_0."""
            res = func_0()
            """Here may be a picec of code to decorate func_0."""
            return res
    	return func_2
    
  7. 至此func_2()除参数部分均已大致确定,最后考虑func_2()的参数,若装饰功能对应代码不需要传参数,则为了保证func_2()对参数的兼容性,使用可变长参数作为形参,同理func_0()也可能有多种参数组合,我们暂且也用可变长参数作为形参,得到:

    def decorator(func_name):
        def func_2(*args, **kwargs):
            """Here may be a picec of code to decorate func_0."""
            res = func_0(*args, **kwargs))
            """Here may be a picec of code to decorate func_0."""
            return res
    	return func_2
    

装饰器模板

   def outer(func):
       def inner(*args, **kwargs):
           """Here may be a picec of code to decorate func."""
           res = func_0(*args, **kwargs))
           """Here may be a picec of code to decorate func."""
           return res
   	return inner

装饰器语法糖

语法糖会自动将下方紧挨着的函数的函数名当作第一个参数传给@后的函数。例:

def outer(func):
    def inner(*args, **kwargs):
        """Here may be a picec of code to decorate func."""
        res = func_0(*args, **kwargs))
        """Here may be a picec of code to decorate func."""
        return res
	return inner


@outer  # func = outer(func)
def func():
    print('from func')
    return 'func'

func()
posted @ 2022-10-11 21:30  Akazukis  阅读(37)  评论(0)    收藏  举报