闭包和装饰器基本使用方法

    xiaoxiao2023-10-31  164

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

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

    闭包的示例代码: 定义一个外部函数 (1) def func_out(num1): # 定义一个内部函数 (2) def func_inner(num2): # 内部函数使用了外部函数的变量(num1) (3) result = num1 + num2 (4) print('结果是:',result) # 外部函数返回了内部函数,这里返回的内部函数就是闭包 (5) return runc_inner 创建闭包实例 (6) f = func_out(1) 执行闭包 (7) f(2) (8) f(3) 运行结果: 3 4

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

    闭包的作用

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

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

    小结: 1. 当返回的内部函数使用了外部函数的变量就形成了闭包 2. 闭包可以对外部函数的变量进行保存

    1. 实现闭包的标准格式: # 外部函数 def test1(a): b = 10 # 内部函数 def test2(): # 内部函数使用了外部函数的变量或者参数 print(a, b) # 返回内部函数, 这里返回的内部函数就是闭包实例 return test2

    闭包的使用

    案例

    需求: 根据配置信息使用闭包实现不同人的对话信息,例如对话:

    张三: 到北京了吗? 李四: 已经到了,放心吧。

    实现: 定义外部函数接收不同的配置信息参数,参数是人名 定义内部函数接收对话信息参数 在内部函数里面把配置信息和对话信息进行拼接输出

    代码

    # 外部函数 def config_name(name): # 内部函数 def say_info(info): print(name + ": " + info) return say_info tom = config_name("Tom") tom("你好!") tom("你好, 在吗?") jerry = config_name("jerry") jerry("不在, 不和你玩!") 闭包案例说明: 闭包还可以提高代码的可重用性,不需要再手动定义额外的功能函数。

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

    修改闭包内使用的外部变量代码: 定义一个外部函数 def func_out(num1): # 定义一个内部函数 def func_inner(num2): # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1 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)

    小结 修改闭包内使用的外部函数变量使用 nonlocal 关键字来完成。

    装饰器

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

    装饰器的功能特点: 不修改已有函数的源代码 不修改已有函数的调用方式 给已有函数增加额外的功能

    装饰器的示例代码 # 添加一个登陆验证码的功能 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. 装饰器的语法糖写法

    # 添加一个登录验证的功能 def check(fn): print("装饰器函数执行了") def inner(): print("请先登录....") fn() return inner # 使用语法糖方式来装饰函数 @check def comment(): print("发表评论") comment() 说明: @check 等价于 comment = check(comment) 装饰器的执行时间是加载模块时立即执行。 执行结果: 请先登录.... 发表评论

    小结

    装饰器本质上就是一个闭包函数,它可以对已有函数进行额外的功能扩展。

    装饰器的语法格式: # 装饰器 # def decorator(fn): # fn:被装饰的目标函数. # def inner(): # '''执行函数之前''' # fn() # 执行被装饰的目标函数 # '''执行函数之后''' # return inner 装饰器的语法糖用法: @装饰器名称,同样可以完成对已有函数的装饰操作。

    装饰器的使用

    1. 函数执行时间的统计 2. 输出日志信息 装饰器实现已有函数执行时间的统计 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

    装饰器的作用: 在不改变已有函数源代码及调用方式的前提下,对已有函数进行功能的扩展。

    通用装饰器的使用

    1. 装饰带有参数的函数 # 添加输出日志的功能 def logging(fn): def inner(num1, num2): print("--正在努力计算--") fn(num1, num2) return inner # 使用装饰器装饰函数 @logging def sum_num(a, b): result = a + b print(result) sum_num(1, 2) 运行结果: --正在努力计算-- 3 2. 装饰带有返回值的函数 # 添加输出日志的功能 def logging(fn): def inner(num1, num2): print("--正在努力计算--") result = fn(num1, num2) return result return inner # 使用装饰器装饰函数 @logging def sum_num(a, b): result = a + b return result result = sum_num(1, 2) print(result) 运行结果: --正在努力计算-- 3 3. 装饰带有不定长参数的函数 # 添加输出日志的功能 def logging(fn): def inner(*args, **kwargs): print("--正在努力计算--") fn(*args, **kwargs) return inner # 使用语法糖装饰函数 @logging def sum_num(*args, **kwargs): result = 0 for value in args: result += value for value in kwargs.values(): result += value print(result) sum_num(1, 2, a=10) 运行结果: --正在努力计算-- 13 4. 通用装饰器 # 添加输出日志的功能 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

    小结 通用装饰器的语法格式:

    # 通用装饰器 def logging(fn): def inner(*args, **kwargs): print("--正在努力计算--") result = fn(*args, **kwargs) return result return inner

    多个装饰器的使用

    1. 多个装饰器的使用示例代码 def makebold(fn): def inner(): print("---1---") return "<b>"+fn()+"</b>" return inner def maketell(fn): def inner(): print("---2---") return "<p>"+fn()+"</p >" return inner @makebold @maketell def test1(): print("---3---") return "hello world" # test1() ret=test1() print(ret) 代码说明: 多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程

    小结: 多个装饰器可以对函数进行多个功能的装饰,装饰顺序是由内到外的进行装饰

    带有参数的装饰器

    在装饰器外面再包裹上一个函数,让最外面的函数接收参数,返回的是装饰器,因为@符号后面必须是装饰器实例。

    # 添加输出日志的功能 def logging(flag): def decorator(fn): def inner(num1, num2): if flag == "+": print("--正在努力加法计算--") elif flag == "-": print("--正在努力减法计算--") result = fn(num1, num2) return result return inner # 返回装饰器 return decorator # 使用装饰器装饰函数 @logging("+") def add(a, b): result = a + b return result @logging("-") def sub(a, b): result = a - b return result result = add(1, 2) print(result) result = sub(1, 2) print(result)

    小结: 使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用

    类装饰器的使用

    类装饰器的介绍 装饰器还有一种特殊的用法就是类装饰器,就是通过定义一个类来装饰函数。

    类装饰器示例代码:

    class Check(object): def __init__(self, fn): # 初始化操作在此完成 self.__fn = fn # 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。 def __call__(self, *args, **kwargs): # 添加装饰功能 print("请先登陆...") self.__fn() @Check def comment(): print("发表评论") comment()

    代码说明: 1. @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。 2. 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。 3. 在call方法里进行对fn函数的装饰,可以添加额外的功能。

    小结 1. 想要让类的实例对象能够像函数一样进行调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable) 2. 类装饰器装饰函数功能在call方法里面进行添加

    最新回复(0)