python进阶篇-day04-闭包与装饰器

news2024/12/29 9:47:30

day04闭包装饰器

函数参数

函数名作为对象

细节

  1. Python是一门以 面向对象为基础的语言, 一切皆对象, 所以: 函数名也是对象.

  2. 直接打印函数名, 打印的是函数的地址. 函数名()则是在调用函数.

  3. 函数名可以作为对象使用, 所以它可以像变量一样赋值, 且赋值后的 变量名() 和 调用 原函数名() 效果是一样的.

格式
def func():
    print('hello word')
    
f = func
f() # hello word

演示

# 需求: 定义1个无返回值的函数func01(), 并直接输出函数名观察效果.
​
# 1. 定义函数.
def func01():
    print('hello world!')
    # return None
​
​
# 在main函数中测试
if __name__ == '__main__':
    # 2. 直接打印函数名, 打印的是函数的地址.  说明 函数名 = 对象
    print(func01)   # <function func01 at 0x000002069305A200>
​
    # 3. 写 函数名() 则是在调用函数.
    func01()        # hello world
    print('-' * 21)
    print(func01()) # hello world先输出, 再打印None
    print('-' * 21)
​
    # 4. 函数名可以作为对象使用, 所以它可以像变量一样赋值, 且赋值后的 变量名()  和 调用 原函数名() 效果是一样的.
    f = func01      # 把 函数名(函数的地址) 赋值给变量f
    print(f)        # <function func01 at 0x000002069305A200>
    f()             # hello world
    print('-' * 21)
    print(f())      # hello world先输出, 再打印None

函数名作为实参传递

函数名的作用

  1. 函数名可以作为对象, 像变量那样赋值.

  2. 函数名可以作为实参进行传递.

代码
# 需求: 定义1个无参函数method(), 定义1个带1个参数的函数func(), 把method()的函数名作为参数传递给func(), 并观察结果.
​
# 1. 定义1个无参函数method()
def method():
    print('我是 method 函数')
​
def get_sum(a, b):
    print('get_sum函数: 加法运算')
    return a + b
​
def get_subtract(a, b):
    print('get_subtract函数: 减法运算')
    return a - b
​
# 2. 定义1个带1个参数的函数func()
def func(fn_name):
    """
    接收函数名(函数对象), 然后调用该函数的.
    :param fn_name: 接收到的函数名(即: 函数对象)
    :return: 无
    """
    print('我是 func 函数')
    fn_name()
​
# 模拟自定义的计算器.
def my_calculation(a, b, fn_name):
    """
    接收两个整数 和 具体的计算规则, 对整数进行对应的计算.
    :param a: 要操作的第1个整数
    :param b: 要操作的第2个整数
    :param fn_name: 具体的计算规则
    :return: 计算结果.
    """
    return fn_name(a, b)
​
# 3. 在main函数中测试.
if __name__ == '__main__':
    # 演示: 函数名可以作为实参进行传递
    func(method)
    print('-' * 21)
​
    # 加法
    result1 = my_calculation(10, 3, get_sum)
    print(f'result1: {result1}')    # 13
    print('-' * 21)
​
    # 减法
    result2 = my_calculation(10, 3, get_subtract)
    print(f'result2: {result2}')    # 7

闭包

介绍

概述

属于python的一种独有写法, 可以实现,: 对外部变量进行临时存储

回顾

函数内的局部变量在函数调用结束后生命周期结束

作用

延长函数内(外部函数) 局部变量的生命周期

构成条件

  1. 有嵌套 外部函数内嵌套 内部函数

  2. 有引用 在内部函数中使用 外部函数的变量

  3. 有返回 在外部函数中, 返回内部函数名, 即: 内部函数 对象

格式
def 外部函数名(形参列表):        
    ......
    def 内部函数名(形参列表):    # 有嵌套
        ......
        使用外部函数的变量       # 有引用
    return 内部函数名           # 有返回

入门代码

def fn_outer(num1):
    def fn_inner(num2):             # 有嵌套
        sum = num1 + num2           # 有引用
        print(f'求和结果:{sum}')
    return fn_inner                 # 有返回
​
​
if __name__ == '__main__':
    fn = fn_outer(10)
    fn(10)
    fn(11)
    fn(12)
​

执行流程图解

nonlocal介绍

概述

nonlocal: 可以实现在内部函数中, 修改外部函数的 变量值

类似于global关键字

给内部函数赋权, 使其可以修改外部函数的变量值

演示

def fn_outer():
    a = 100
    def fn_inner():
        nonlocal a      # 使用nonlocal关键字
        a += 10         # 修改外部函数的变量
        print(f'a:{a}')
    return fn_inner
​
​
if __name__ == '__main__':
    fn = fn_outer()
    fn()    # a:110
    fn()    # a:120
    fn()    # a:130
图解

装饰器

在不改变原有函数的基础上给原有函数增加额外的功能

介绍

概述

装饰器也是闭包的一种, 即: 装饰器 = 闭包 + 额外的功能

前提

  1. 有嵌套

  2. 有引用

  3. 有返回

  4. 有额外功能

格式
def 外部函数(形参列表):
    ......
    def 内部函数(形参列表):
        功能扩展
        ......
        使用外函数变量
    return 内部函数名

用法

方式1: 传统用法

​ 变量名 = 装饰器名(被装饰函数名) => 变量名一般为被装饰函数名

​ 变量名()

方式2: 语法糖

​ 在被装饰的函数上, 写: @装饰器名, 之后跟调用普通函数方式一样

代码演示

# 定义装饰器, 增加登录功能
def check_login(fn_name):
    def inner():
        print('登陆中...')
        fn_name()
​
    return inner
​
​
# 定义评论功能
@check_login
def comment():
    print('评论成功!!!')
​
​
if __name__ == '__main__':
    # 方式1, 传统写法
    # comment = check_login(comment)
    # comment()
    
    # 方式2, 语法糖
    comment()
​
​

装饰器的用法

装饰器的内部函数 格式要和 被装饰函数的格式保持一致 即:

被装饰函数是无参无返回值, 则: 装饰器的内部函数 也是 无参无返回值

被装饰函数是有参无返回值, 则: 装饰器的内置函数 也是 有参无返回值

......

无参无返回

# 需求: 在无参无返回值的原有求和函数前, 添加1个友好提示, 即: 正在努力计算中...
​
# 1. 装饰器.
def print_info(fn_name):    # fn_name: 要被装饰的函数名(原函数名)
    def fn_inner():         # 有嵌套.  装饰器的内部函数 格式 要和 原函数 格式保持一致.
        print('[友好提示] 正在努力计算中...')  # 有额外功能
        fn_name()                          # 有引用
    return fn_inner         # 有返回
​
​
# 2. 要被装饰的函数, 即: 原函数(这里是: 无参无返回值的)
@print_info
def get_sum():
    a = 10
    b = 20
    # 求和
    sum = a + b
    # 打印结果.
    print(f'sum: {sum}')
​
# 在main函数中测试调用.
if __name__ == '__main__':
    # 装饰器 写法1: 传统写法.
    # get_sum = print_info(get_sum)
    # get_sum()
​
    # 装饰器 写法2: 语法糖
    get_sum()

有参无返回

# 需求: 在有参无返回值的原有求和函数前, 添加1个友好提示, 即: 正在努力计算中...
​
# 1. 装饰器.
def print_info(fn_name):    # fn_name: 要被装饰的函数名(原函数名)
    def fn_inner(a, b):         # 有嵌套.  装饰器的内部函数 格式 要和 原函数 格式保持一致.
        print('[友好提示] 正在努力计算中...')  # 有额外功能
        fn_name(a, b)                      # 有引用
    return fn_inner         # 有返回
​
​
# 2. 要被装饰的函数, 即: 原函数(这里是: 有参无返回值的)
@print_info
def get_sum(a, b):
    # 求和
    sum = a + b
    # 打印结果.
    print(f'sum: {sum}')
​
# 在main函数中测试调用.
if __name__ == '__main__':
    # 装饰器 写法1: 传统写法.
    # get_sum = print_info(get_sum)
    # get_sum(100, 200)
​
    # 装饰器 写法2: 语法糖
    get_sum(10, 3)

无参有返回

# 需求: 在无参有返回值的原有求和函数前, 添加1个友好提示, 即: 正在努力计算中...
​
# 1. 装饰器.
def print_info(fn_name):  # fn_name: 要被装饰的函数名(原函数名)
    def fn_inner():  # 有嵌套.  装饰器的内部函数 格式 要和 原函数 格式保持一致.
        print('[友好提示] 正在努力计算中...')  # 有额外功能
        return fn_name()  # 有引用
​
    return fn_inner  # 有返回
​
​
# 2. 要被装饰的函数, 即: 原函数(这里是: 无参有返回值的)
@print_info
def get_sum():
    a, b = 10, 3
    # 求和
    sum = a + b
    # 返回结果.
    return sum
​
​
# 在main函数中测试调用.
if __name__ == '__main__':
    # 装饰器 写法1: 传统写法.
    # get_sum = print_info(get_sum)
    # sum1 = get_sum()
    # print(f'sum1: {sum1}')
​
    # 装饰器 写法2: 语法糖
    sum2 = get_sum()
    print(f'sum2: {sum2}')

有参有返回

# 定义装饰器, 装饰有参数有返回值的函数
def print_info(fn_name):
    def inner(a, b):
        print('正在计算中')
        return fn_name(a, b)
​
    return inner
​
​
# @print_info
def get_sum(a, b):
    num_sum = a + b
    return num_sum
​
​
if __name__ == '__main__':
    # 调用方式1: 语法糖
    # print(get_sum(10, 20))
​
    # 调用方式2: 传统方式
    get_sum = print_info(get_sum)
    print(get_sum(10, 20))

装饰不定长参数

# 定义装饰器装饰带有不定长参数的函数
def print_info(fn_name):
    def inner(*args, **kwargs):
        print('正在努力计算中')
        return fn_name(*args, **kwargs)
​
    return inner
​
# 定义函数, 同时接收不定长位置参数和关键字参数, 遍历累加求和
@print_info
def get_sum(*args, **kwargs):
    num_sum = 0
    # 遍历位置参数并求和
    for i in args:
        num_sum += i
    # 遍历关键字参数并求和
    for j in kwargs.values():
        num_sum += j
    return num_sum
​
​
if __name__ == '__main__':
    # 调用方式1
    # get_sum = print_info(get_sum)
    # print(get_sum(1, 2, 3, 4, a=5, b=6, c=7))
​
    # 调用方式2
    print(get_sum(1, 2, 3, 4, a=5, b=6, c=7))

多装饰器装饰1个函数

由内到外装饰(传统写法), 从上到下执行(装饰器)

代码
def check_login(fn_name):
    def inner():
        print('登录校验中...')
        fn_name()
​
    return inner
​
​
def check_code(fn_name):
    def inner():
        print('登录校验中...')
        fn_name()
​
    return inner
​
​
@check_login
@check_code
def comment():
    print('评论成功!!!')
​
​
if __name__ == '__main__':
    # 方式1
    # cc = check_code(comment)
    # cl = check_login(cc)
    # cl()
    
    # 方式2
    comment()
图解

带有参数的装饰器(一个装饰器装饰多个函数)

一个装饰器的参数只能有一个

如果装饰器有多个参数, 则需要在该装饰器的外边再定义一层函数, 用于封装多个参数, 并返回装饰器

格式
def logging(flag):
    def decorator(fn_name):
        def inner():
            if flag == '+':
                print('正在努力计算加法中')
            elif flag == '-':
                print('正在努力计算减法中')
            fn_name()
​
        return inner
​
    return decorator
​
​
@logging('+')
def sum_add():
    a, b = 1, 2
    print(f'求和结果为: {a + b}')
​
​
@logging('-')
def subtract():
    a, b = 1, 2
    print(f'求和结果为: {a - b}')
​
​
if __name__ == '__main__':
    sum_add()
    subtract()
改进

通过对象的__ name __属性实现

* 代码:

def decorator(fn_name):
    def inner():
        if fn_name == 'sum_add':
            print('正在努力计算加法中')
        elif fn_name == 'subtract':
            print('正在努力计算减法中')
        fn_name()
    return inner
​
​
@decorator
def sum_add():
    a, b = 1, 2
    print(f'求和结果为: {a + b}')
​
    
@decorator
def subtract():
    a, b = 1, 2
    print(f'求和结果为: {a - b}')
​
​
if __name__ == '__main__':
    sum_add()
    subtract()

扩展:类装饰器(了解)

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

class 类装饰器():
    # 装饰器代码
​
@类装饰器名称
def 函数():
    # 函数代码

举个栗子:编写一个Check类装饰器,用于实现用户的权限验证

'''
类装饰器编写规则:
① 必须有一个__init__初始化方法,用于接收要装饰函数的函数 
② 必须把这个类转换为可以调用的函数
问题:如何把一个类当做一个装饰器函数进行调用(把类当做函数)
'''
​
class Check():
    def __init__(self, fn):
        # fn就是要修饰函数的名称,当Check装饰器类被调用时,系统会自动把comment函数名称传递给fn变量
        self.__fn = fn
    # __call__方法:把一个类转换为函数的形式进行调用
    def __call__(self, *args, **kwargs):
        # 编写装饰器代码
        print('请先登录')
        # 调用comment函数本身
        self.__fn(*args, **kwargs)
​
# 编写一个函数,用于实现评论功能,底层comment = Check(comment)
@Check
def comment():
    print('评论功能')
​
# 调用comment函数,实现评论功能
comment()

@Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。

要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。

call方法里进行对fn函数的装饰,可以添加额外的功能。

目标:① 了解闭包的作用以及闭包的基本语法(三步走)

​ ② 能独立完成通用装饰器的编写

​ ③ 能使用装饰器传递参数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2098323.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用 BigQuery ML 和 Google Sheets 数据预测电商网站访客趋势

看看如何使用 BigQuery ML 与 Google Sheets 构建时间预测模型&#xff0c;为商业分析提供助力~ 电子表格无处不在&#xff01;作为最实用的生产力工具之一&#xff0c;Google Workspace 的 Sheets 电子表格工具拥有超过 20 亿用户&#xff0c;可让数据的组织、计算和呈现变得轻…

如何完整删除rancher中已接入的rancher集群并重新导入

前提&#xff1a;如果手动删除kubectl delete all --all --namespace<namespace>删除不了的情况下可以使用此方案 一&#xff1a;查找rancher接入集群的所有namespace 接入rancher的k8s集群namespace都是以cattle命名的 rootA800-gpu-node01:~# kubectl get namespaces |…

32位Win7+64位Win10双系统教程来袭,真香!

前言 前段时间整了很多关于Windows双系统的教程&#xff0c;但基本都是UEFI引导启动的方式&#xff0c;安装的系统要求必须是64位Windows。 各种双系统方案&#xff08;点我跳转&#xff09; 今天咱们就来玩一玩32位 Windows 764位 Windows 10的装机方案&#xff01; 开始之…

逆向工程核心原理 Chapter23 | DLL注入

前面学的只是简单的Hook&#xff0c;现在正式开始DLL注入的学习。 0x01 DLL注入概念 DLL注入指的是向运行中的其它进程强制插入特点的DLL文件。 从技术细节上来说&#xff0c;DLL注入就是命令其它进程自行调用LoadLibrary() API&#xff0c;加载用户指定的DLL文件。 概念示…

PMP–一、二、三模、冲刺、必刷–分类–2.项目运行环境–治理

文章目录 技巧一模2.项目运行环境--4.组织系统--治理--项目组合、项目集和项目治理--项目治理是指用于指导项目管理活动的框架、功能和过程&#xff0c;从而创造独特的产品、服务或结果以满足组织、战略和运营目标。不存在一种治理框架适用于所有组织。组织应根据组织文化、项目…

【Godot4.1】自定义纯绘图函数版进度条控件——RectProgress

概述 一个纯粹基于CanvasItem绘图函数&#xff0c;重叠绘制矩形思路实现的简单进度条控件。2023年7月编写。 之所以将它作为单独的示例发出来&#xff0c;是因为它代表了一种可能性&#xff0c;就是不基于Godot内置的控件&#xff0c;而是完全用绘图函数或其他底层API形式来创…

第二百一十二节 Java反射 - Java构造函数反射

Java反射 - Java构造函数反射 以下四种方法来自 Class 类获取有关构造函数的信息: Constructor[] getConstructors() Constructor[] getDeclaredConstructors() Constructor<T> getConstructor(Class... parameterTypes) Constructor<T> getDeclaredConstructor(…

Apache SeaTunnel 2.3.7发布:全新支持大型语言模型数据转换

我们欣喜地宣布&#xff0c;Apache SeaTunnel 2.3.7 版本现已正式发布&#xff01;作为一个广受欢迎的下一代开源数据集成工具&#xff0c;Apache SeaTunnel 一直致力于为用户提供更加灵活、高效的数据同步和集成能力。此次版本更新不仅引入了如 LLM&#xff08;大型语言模型&a…

python-pptx - Python 操作 PPT 幻灯片

文章目录 一、关于 python-pptx设计哲学功能支持 二、安装三、入门1、你好世界&#xff01;例子2、Bullet 幻灯片示例3、add_textbox()示例4、add_picture()示例5、add_shape()示例6、add_table()示例7、从演示文稿中的幻灯片中提取所有文本 四、使用演示文稿1、打开演示文稿2、…

心觉:潜意识精准显化(二)赚不到钱的困境根源是什么

上一篇文章我讲到了关于潜意识精准显化系列文章&#xff0c;我会以财富的精准显化为例讲解 财富广义的讲有很多&#xff0c;智慧&#xff0c;能力&#xff0c;人生阅历&#xff0c;苦难&#xff0c;高质量的人际关系&#xff0c;金钱等等都算财富 这么多财富类型&#xff0c;…

Pinia 使用(一分钟了解)

Pinia 使用&#xff08;一分钟了解&#xff09; Pinia 官网地址&#xff1a;Pinia 官方文档 文章目录 Pinia 使用&#xff08;一分钟了解&#xff09;一、Pinia是什么二、Vue中如何使用Pinia1. 安装Pinia2. 创建Pinia实例3. 定义一个Store4. 在组件中使用Store5. 模块化和插件 …

C++红黑树的底层原理及其实现原理和实现

小编在学习完红黑树之后&#xff0c;发现红黑树的实现相对于AVL树来说会简单一点&#xff0c;并且大家在学了C中的set和map容器之后&#xff0c;会明白set和map的容器的底层就是运用的红黑树&#xff0c;因为相对于AVL树&#xff0c;红黑树的旋转次数会大大减少&#xff0c;并且…

MySQL笔记(大斌)

乐观锁和悲观锁是什么&#xff1f; 数据库中的并发控制是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。乐观锁和悲观锁是并发控制主要采用的技术手段。 悲观锁&#xff1a;假定会发生并发冲突&#xff0c;会对操作的数据进行加锁&a…

好的渲染农场应该具备哪些功能?

对于3D艺术家和工作室来说&#xff0c;渲染往往是制作过程中最耗时的部分。这一关键阶段需要强大的计算资源和高效的工作流程&#xff0c;以确保生产时间表得以满足。一个好的渲染农场对于提高生产力和确保项目在不牺牲质量的情况下按时完成至关重要。随着对详细3D视觉效果的需…

UEFI——PCD的简单使用

一、PCD的定义及概念 在UEFI固件接口中&#xff0c;PCD&#xff08;Platform Configuration Database&#xff09;是一个用于存储和访问平台特定配置信息的机制。PCD允许UEFI驱动程序和应用程序在运行时获取和设置平台相关的参数&#xff0c;而无需硬编码这些值。PCD变量可以被…

计算机毕业设计推荐-基于Java的网上电子图书管理系统【Java-python-大数据定制】

&#x1f496;&#x1f525;作者主页&#xff1a;毕设木哥 精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; 实战项目 文章目录 实战项目 一、基于Java的网上电子图书管理…

魏牌蓝山智驾版,长城的智能化「大反攻」

‍作者 |老缅 编辑 |德新 8月下旬&#xff0c;魏牌全新蓝山上市&#xff0c;定位「长城首款NOA智能六座旗舰SUV」。 新车分智驾Max和智驾Ultra两个版本&#xff0c;售价分别为29.98万元和32.68万元。 魏建军为蓝山的上市&#xff0c;时隔6年再度回到了发布会的舞台&#xff…

时序预测基础模型又中顶会!真心建议各位往这个方向发论文

时序领域又有新突破啦&#xff01;谷歌最新提出TimesFM&#xff0c;仅需200M参数&#xff0c;零样本预测性能超越有监督&#xff01;成功入选ICML 2024&#xff01; TimesFM是一种全新的时间序列通用基础模型&#xff0c;这类模型相比传统时序模型&#xff0c;拥有整合和利用广…

HDLC 和 PPP 原理与配置

HDLC简介 HDLC协议是一种通用的协议&#xff0c;工作在OSI参考模型的数据链路层。数据报文加上头开销和尾开销后封装成HDLC帧。 HDLC具有以下特点&#xff1a; •HDLC协议只支持点到点链路&#xff0c;不支持点到多点。 •HDLC协议不支持IP地址协商&#xff0c;不支持认证。 •…

【数据结构-二维前缀和】【列维护优化】力扣3212. 统计 X 和 Y 频数相等的子矩阵数量

给你一个二维字符矩阵 grid&#xff0c;其中 grid[i][j] 可能是 ‘X’、‘Y’ 或 ‘.’&#xff0c;返回满足以下条件的 子矩阵 数量&#xff1a; 包含 grid[0][0] ‘X’ 和 ‘Y’ 的频数相等。 至少包含一个 ‘X’。 示例 1&#xff1a; 输入&#xff1a; grid [[“X”,“…