当前位置: 首页 > news >正文

python闭包与装饰器

函数参数

函数名的作用

  • 函数名存放的是函数所在内存空间的地址,即函数名代表函数入口地址
  • 函数名() 执行函数名所存放空间地址上的代码,即 函数名() 代表函数调用,直接调用
  • 函数名可以像普通变量一样赋值,函数值后的结果与原函数名作用是一样的,即函数名可以做函数参数

例:定义一个无返回值的函数func01,并直接输出函数名查看

# 定义函数
def func01():print('func01')
# 函数名() 执行函数名所存放空间地址上的代码
func01() 
# 函数名存放的是函数所在空间上的地址
print(func01) # fun01函数在内存中的地址 <function func01 at 0x000001AE86427880>print(func01()) # None func01() 是调用函数,print打印的是该函数的返回值,无返回值则为None# 函数名也可以像普通变量一样赋值
func02 = func01 # 把 func01 函数的 地址值 赋值给func02,即 func02()=func01() 
func02()

函数作为参数使用

函数名作为实参传递,本质上传递的是对应函数的地址

例:将函数作为实参来传递:

  1. 定义一个无参函数method()
  2. 定义一个有一个参数的函数func()
  3. 把无参函数method()函数名传递给有参函数func(),观察效果
def method():print('--- method函数 ---')def func(fn_name):print('--- func函数 ---')fn_name() # 执行传递进来的fn_name函数if __name__ == '__main__':print('--- 程序开始 ---')func(method) # 把method函数的内存地址传递给fn_nameprint('--- 程序结束 ---')

闭包

当调用完函数后,函数内定义的变量就销毁了,但有时候需要保存函数内的这个变量,并在这个变量的基础上完成一系列的操作

闭包语法

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,像这种使用外部函数变量的内部函数称为闭包

闭包的前提条件

  1. 有嵌套:即有外部函数,有内部函数
  2. 有引用:即在内部函数中,使用外部函数的变量
  3. 有返回:即在外部函数中,返回内部函数(对象)
# 外部函数
def 外部函数名(外部参数):# 内部函数def 内部函数名(内部参数):...[使用玩不函数的变量]return 内部函数名

例:定义一个函数用于保存变量10,然后调用函数返回值变量并重复累加数值

def method():a = 10return adef outer(num1):def inner(num2):sum = num1 + num2print(f'求和结果为:{sum}')return inner # 返回inner函数的内存地址,此时inner函数没有被执行if __name__ == '__main__':print('--- 程序开始 ---')# 把outer函数的内存地址赋值给fnfn = outer(10) #fn = outer函数的返回值 = inner这个内部函数对象print(fn) # 地址值print(fn(1)) # 11print(fn(1)) # 11print(fn(1)) # 11

nolocal关键字

nolocal 用于嵌套函数中,允许内部函数修改外部函数(非全局)作用域的变量。要求变量在外层函数中已定义并赋值,且不能在内部函数中首次赋该变量值。

def outer():a = 100def inner():# nonlocal a # 声明a为外部函数的局部变量nonlocal aa += 1print(f'--- inner函数 --- a = {a}')return innerif __name__ == '__main__':print('--- 程序开始 ---')fn = outer()fn() # 101fn() # 102fn() # 103

装饰器

装饰器的作用是不改变原有函数的基础上,给原有函数增加额外功能,其本质上就是一个闭包函数

装饰器的构成条件

  1. 有嵌套:在函数嵌套(函数里面再定义函数) 的前提下
  2. 有引用:内部函数使用了外部函数的变量(还包括外部函数的参数)
  3. 有返回:外部函数返回了内部函数名
  4. 有额外功能:给需要装饰的原有函数增加了额外功能
# 1. 定义装饰器(闭包函数),用于对指定函数的功能 进行扩展
def check_login(fn_name):# 2. 定义内部函数,用于对指定的 fn_name函数 进行功能扩展def inner():print('--- 登录验证 ---')fn_name()  # 调用原函数return inner  # 返回内部函数的引用# 3. 定义函数
@check_login  # 装饰器写法2:@装饰器名
def comment():print('--- 发布评论 ---')# 4. 调用装饰器,传入被装饰的函数,返回一个新的函数对象
if __name__ == '__main__':print('--- 程序开始 ---')# 装饰器写法1:变量= 装饰器名(要被装饰的原函数名)# hg = check_login(comment)# hg()comment()  # 调用装饰器,传入被装饰的函数,返回一个新的函数对象

装饰器的使用

无参无返回值的函数

# 在无参无返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...def print_info(fn):def inner():print('正在努力计算中...')fn()return inner@print_info
def sum_num():a = 10b = 20result = a + bprint(f'计算结果是:{result}')if __name__ == '__main__':sum_num()

有参无返回值的函数

# 在有参无返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...def print_info(fn):def inner(x,y):print('正在努力计算中...')fn(x,y)return inner@print_info
def sum(x,y):z = x + yprint(f'计算结果是:{z}')if __name__ == '__main__':sum(10,20)

无参有返回值的函数

# 在无参有返回值的原有函数求和计算结果之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...def print_info(fn):def inner():print('正在努力计算中...')result = fn()  # 调用原函数,获取结果return result  # 返回结果return inner@print_info
def sum():a = 10b = 20result = a + breturn resultif __name__ == '__main__':result = sum()  # 调用装饰器,传入被装饰的函数,返回一个新的函数对象print(f'计算结果是:{result}')

有参有返回值的函数

# 在有参有返回值的原有函数求和计算之前,添加一个友好提示(注意:不能改变源码):正在努力计算中...def print_info(fn):def inner(a, b):  print('正在努力计算中...')result = fn(a, b)  return result return inner@print_info
def sum_num(x, y):z = x + yreturn zif __name__ == '__main__':result = sum_num(10, 20)print(f'计算结果是:{result}')

通用装饰器(函数参数不定长,有返回值)

# 定义一个可以计算多个数据和多个字典value值之和的函数,并调用。在原有函数的计算结果之前,添加一个友好提示:正在努力计算中...def print_info(func):def inner(*args, **kwargs):  print("正在努力计算中...")result = func(*args, **kwargs)  return resultreturn inner# *args 元组类型
# **kwargs 字典类型
def get_sum(*args, **kwargs):result = 0for value in args:result += valuefor value in kwargs.values():result += valuereturn resultif __name__ == '__main__':result = get_sum(1, 2, 3, 4, 5, a=10, b=20, c=30)print(f"计算结果是:{result}")

多个装饰器装饰一个函数

多个装饰器的装饰过程是:离函数最近的装饰器先装饰,然后外面的装饰器在进行装饰,由内到外的装置过程

例:发表评论之前,需要先登录用户,再进行校验验证码。先定义有发哦表评论的个功能函数,然后在不改变原有函数的基础上,需要先检查用户登录和输入验证码

def check_user(fn):def inner():print('输入用户名和密码')print('检查用户名和密码')print('登录成功')fn()return innerdef check_code(fn):def inner():print('校验登录验证码')fn()return inner@check_user
@check_code
def comment():print('发表评论')
if __name__ == '__main__':comment()"""
输入用户名和密码
检查用户名和密码
登录成功
校验登录验证码
发表评论
""" 

带参数的装饰器

使用带参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回装饰器

def logging(flag):def decorator(fn):def inner(a,b):if flag == '+':print('正在进行加法运算')elif flag == '-':print('正在进行减法运算')result = fn(a,b)return resultreturn innerreturn decorator@logging('+')
def add(a,b):result = a+breturn result@logging('-')
def sub(a,b):result = a-breturn resultif __name__ == '__main__':add_result = add(1,2)print(f"加法计算结果是:{add_result}")sub_result = sub(5,3)   print(f"减法计算结果是:{sub_result}")
http://www.xdnf.cn/news/897697.html

相关文章:

  • Chrome安装代理插件ZeroOmega(保姆级别)
  • 浏览器工作原理01 [#]Chrome架构:仅仅打开了1个页面,为什么有4个进程
  • .NET AOT 详解
  • Spring AI 入门:Java 开发者的生成式 AI 实践之路
  • 保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
  • C#报错 iText.Kernel.Exceptions.PdfException: ‘Unknown PdfException
  • uniapp+vue2解构赋值和直接赋值的优缺点
  • 12.vite,webpack构建工具
  • 云原生玩法三问:构建自定义开发环境
  • 父组件prop传向子组件的值,被子组件直接v-model绑定 功能不生效
  • win10安装WSL2、Ubuntu24.04
  • 华为云学堂-云原生开发者认证课程列表
  • uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
  • 惊艳呈现:探索数据可视化的艺术与科学
  • 详细介绍uni-app中Composition API和Options API的使用方法
  • 微信小程序- 用canvas生成排行榜
  • iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
  • 大白话解释一下 MIC Bias
  • React - 组件通信
  • CDN:静态资源如何加速?
  • 采用轮询的方式实现在线人数
  • SpringAI 1.0.0 正式版——利用Redis存储会话(ChatMemory)
  • Kafka 入门指南与一键部署
  • SpringCloud学习笔记-3
  • Linux命令基础(2)
  • 软件功能测试目的是啥?如何通过测试用例确保产品达标?
  • <2>-MySQL库的操作
  • Python 字典(dict)的高级用法与技巧
  • 跨平台游戏引擎 Axmol-2.6.1 发布
  • [论文阅读] 人工智能 | 利用负信号蒸馏:用REDI框架提升LLM推理能力