跨境互联网 跨境互联网
首页
  • AI 工具

    • 绘图提示词工具 (opens new window)
    • ChatGPT 指令 (opens new window)
  • ChatGPT

    • ChatGP T介绍
    • ChatGPT API 中文开发手册
    • ChatGPT 中文调教指南
    • ChatGPT 开源项目
  • Midjourney

    • Midjourney 文档
  • Stable Diffusion

    • Stable Diffusion 文档
  • 其他

    • AIGC 热门文章
    • 账号合租 (opens new window)
    • 有趣的网站
  • Vue

    • Vue3前置
  • JAVA基础

    • Stream
    • Git
    • Maven
    • 常用第三方类库
    • 性能调优工具
    • UML系统建模
    • 领域驱动设计
    • 敏捷开发
    • Java 测试
    • 代码规范及工具
    • Groovy 编程
  • 并发编程&多线程

    • 并发编程
    • 高性能队列 Disruptor
    • 多线程并发在电商系统下的应用
  • 其他

    • 面试题
  • 消息中间中间件

    • Kafka
    • RabbitMQ
    • RocketMQ
  • 任务调度

    • Quartz
    • XXL-Job
    • Elastic-Job
  • 源码解析

    • Mybatis 高级使用
    • Mybatis 源码剖析
    • Mybatis-Plus
    • Spring Data JPA
    • Spring 高级使用
    • Spring 源码剖析
    • SpringBoot 高级使用
    • SpringBoot 源码剖析
    • Jdk 解析
    • Tomcat 架构设计&源码剖析
    • Tomcat Web应用服务器
    • Zookeeper 高级
    • Netty
  • 微服务框架

    • 分布式原理
    • 分布式集群架构场景化解决方案
    • Dubbo 高级使用
    • Dubbo 核心源码剖析
    • Spring Cloud Gateway
    • Nacos 实战应用
    • Sentinel 实战应用
    • Seata 分布式事务
  • 数据结构和算法的深入应用
  • 存储

    • 图和Neo4j
    • MongoDB
    • TiDB
    • MySQL 优化
    • MySQL 平滑扩容实战
    • MySQL 海量数据存储与优化
    • Elasticsearch
  • 缓存

    • Redis
    • Aerospike
    • Guava Cache
    • Tair
  • 文件存储

    • 阿里云 OSS 云存储
    • FastDF 文件存储
  • 基础

    • Linux 使用
    • Nginx 使用与配置
    • OpenResty 使用
    • LVS+Keepalived 高可用部署
    • Jekins
  • 容器技术

    • Docker
    • K8S
    • K8S
  • 01.全链路(APM)
  • 02.电商终极搜索解决方案
  • 03.电商亿级数据库设计
  • 04.大屏实时计算
  • 05.分库分表的深入实战
  • 06.多维系统下单点登录
  • 07.多服务之间分布式事务
  • 08.业务幂等性技术架构体系
  • 09.高并发下的12306优化
  • 10.每秒100W请求的秒杀架构体系
  • 11.集中化日志管理平台的应用
  • 12.数据中台配置中心
  • 13.每天千万级订单的生成背后痛点及技术突破
  • 14.红包雨的架构设计及源码实现
  • 人工智能

    • Python 笔记
    • Python 工具库
    • 人工智能(AI) 笔记
    • 人工智能(AI) 项目笔记
  • 大数据

    • Flink流处理框架
  • 加密区

    • 机器学习(ML) (opens new window)
    • 深度学习(DL) (opens new window)
    • 自然语言处理(NLP) (opens new window)
AI 导航 (opens new window)

Revin

首页
  • AI 工具

    • 绘图提示词工具 (opens new window)
    • ChatGPT 指令 (opens new window)
  • ChatGPT

    • ChatGP T介绍
    • ChatGPT API 中文开发手册
    • ChatGPT 中文调教指南
    • ChatGPT 开源项目
  • Midjourney

    • Midjourney 文档
  • Stable Diffusion

    • Stable Diffusion 文档
  • 其他

    • AIGC 热门文章
    • 账号合租 (opens new window)
    • 有趣的网站
  • Vue

    • Vue3前置
  • JAVA基础

    • Stream
    • Git
    • Maven
    • 常用第三方类库
    • 性能调优工具
    • UML系统建模
    • 领域驱动设计
    • 敏捷开发
    • Java 测试
    • 代码规范及工具
    • Groovy 编程
  • 并发编程&多线程

    • 并发编程
    • 高性能队列 Disruptor
    • 多线程并发在电商系统下的应用
  • 其他

    • 面试题
  • 消息中间中间件

    • Kafka
    • RabbitMQ
    • RocketMQ
  • 任务调度

    • Quartz
    • XXL-Job
    • Elastic-Job
  • 源码解析

    • Mybatis 高级使用
    • Mybatis 源码剖析
    • Mybatis-Plus
    • Spring Data JPA
    • Spring 高级使用
    • Spring 源码剖析
    • SpringBoot 高级使用
    • SpringBoot 源码剖析
    • Jdk 解析
    • Tomcat 架构设计&源码剖析
    • Tomcat Web应用服务器
    • Zookeeper 高级
    • Netty
  • 微服务框架

    • 分布式原理
    • 分布式集群架构场景化解决方案
    • Dubbo 高级使用
    • Dubbo 核心源码剖析
    • Spring Cloud Gateway
    • Nacos 实战应用
    • Sentinel 实战应用
    • Seata 分布式事务
  • 数据结构和算法的深入应用
  • 存储

    • 图和Neo4j
    • MongoDB
    • TiDB
    • MySQL 优化
    • MySQL 平滑扩容实战
    • MySQL 海量数据存储与优化
    • Elasticsearch
  • 缓存

    • Redis
    • Aerospike
    • Guava Cache
    • Tair
  • 文件存储

    • 阿里云 OSS 云存储
    • FastDF 文件存储
  • 基础

    • Linux 使用
    • Nginx 使用与配置
    • OpenResty 使用
    • LVS+Keepalived 高可用部署
    • Jekins
  • 容器技术

    • Docker
    • K8S
    • K8S
  • 01.全链路(APM)
  • 02.电商终极搜索解决方案
  • 03.电商亿级数据库设计
  • 04.大屏实时计算
  • 05.分库分表的深入实战
  • 06.多维系统下单点登录
  • 07.多服务之间分布式事务
  • 08.业务幂等性技术架构体系
  • 09.高并发下的12306优化
  • 10.每秒100W请求的秒杀架构体系
  • 11.集中化日志管理平台的应用
  • 12.数据中台配置中心
  • 13.每天千万级订单的生成背后痛点及技术突破
  • 14.红包雨的架构设计及源码实现
  • 人工智能

    • Python 笔记
    • Python 工具库
    • 人工智能(AI) 笔记
    • 人工智能(AI) 项目笔记
  • 大数据

    • Flink流处理框架
  • 加密区

    • 机器学习(ML) (opens new window)
    • 深度学习(DL) (opens new window)
    • 自然语言处理(NLP) (opens new window)
AI 导航 (opens new window)
  • Python介绍
  • 基础

  • 进阶

    • Python模块与包
    • Python面向对象
    • Python面向对象高级
    • Python高级语法
    • Python正则表达式
    • Python进程和线程
    • Python闭包与装饰器
      • 闭包
        • 作用域
        • 变量的作用域
        • 全局变量与局部变量的访问范围
        • 问题:为什么在全局作用域中无法访问局部变量
        • 问题:我们有没有办法把函数内部的局部变量保留
        • 闭包的构成条件(三步走)
        • 注意事项
        • 在闭包的内部实现对外部变量的修改
        • 闭包的综合示例
      • 装饰器
        • 什么是装饰器
        • 装饰器的雏形
        • 装饰器定义
        • 装饰器的作用:获取程序的执行时间
        • 带有参数装饰器
        • 带有返回装饰器
        • 通用版本的装饰器(以后所有的装饰器以此为准)
        • 装饰器高级:使用装饰器传递参数(了解)
        • 扩展:类装饰器(了解)
  • 其他

  • Python开源项目
  • Python
  • 进阶
Revin
2023-04-16
目录

Python闭包与装饰器

# 闭包

# 作用域

在Python代码中,作用域分为两种情况:全局作用域 与 局部作用域

# 变量的作用域

在全局定义的变量 => 全局变量

在局部定义的变量 => 局部变量

# 全局变量与局部变量的访问范围

① 在全局作用域中可以访问全局变量,在局部作用域中可以访问局部变量

# 全局作用域(全局变量)
num1 = 10
def func():
    # 局部作用域(局部变量)
    num2 = 20
    # ① 在局部访问局部变量
    print(num2)

# ① 在全局访问全局变量
print(num1)
# 调用函数
func()
1
2
3
4
5
6
7
8
9
10
11
12

② 在局部作用域中可以访问全局变量

# 全局作用域(全局变量)
num1 = 10
def func():
    # 局部作用域(局部变量)
    # ② 在局部作用域中可以访问全局变量
    print(num1)

# 调用函数
func()
1
2
3
4
5
6
7
8
9

③ 在全局作用域中不能访问局部变量

# 全局作用域(全局变量)
num1 = 10
def func():
    # 局部作用域(局部变量)
    num2 = 20

# 调用函数
func()
# 在全局作用域中调用局部变量num2
print(num2)
1
2
3
4
5
6
7
8
9
10

运行结果:

image-20230416173820802

# 问题:为什么在全局作用域中无法访问局部变量

答:主要原因在于,在Python的底层存在一个“垃圾回收机制”,主要的作用就是回收内存空间。加快计算机的运行。我们在Python代码中定义的变量也是需要占用内存的,所以Python为了回收已经被已经过的内存,会自动将函数运行以后的内部变量和程序直接回收。

# 问题:我们有没有办法把函数内部的局部变量保留

答:使用闭包

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

# 闭包的构成条件(三步走)

第一步:有嵌套

第二步:有引用

第三步:有返回(return)

'''
闭包程序三步走:① 有嵌套 ② 有引用 ③ 有返回
'''

def func():
    num = 20  # 局部变量
    def inner():
        print(num)
    return inner  # 实际上inner函数并没有执行,只是返回了inner函数在内存中的地址

f = func()  # 相当于把inner在内存中的地址0x7fbc9b3f8e18赋值给变量f
f()  # 找到inner函数的内存地址,并执行器内部的代码(num=20),在于闭包函数保留了num=20这个局部变量
1
2
3
4
5
6
7
8
9
10
11
12

闭包的作用:正常情况下,当执行func()的时候,函数内部的变量num = 20,会随着函数的func函数的结束而被垃圾回收机制所回收。所以闭包的真正作用:就是可以在全局作用域中,实现间接对局部变量进行访问。

# 注意事项

注意点:

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

# 在闭包的内部实现对外部变量的修改

错误版本:

'''
Python闭包:① 有嵌套 ② 有引用 ③ 有返回
'''

def outer():
    num = 10
    def inner():
        # 这种写法无法实现通过闭包修改外部的局部变量
        num = 20
    print('outer函数中的num:', num)  # 10
    inner()  # 执行函数inner,让num=20生效
    print('outer函数中的num:', num)  # 10
    return inner

f = outer()
f()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

正确版本:

新知识点:nonlocal关键字(在函数内部修改函数外部的变量,这个变量非全局变量)

老知识点:global关键字(在函数内部声明变量,代表引用全局作用域中的全局变量)

'''
Python闭包:① 有嵌套 ② 有引用 ③ 有返回
'''

def outer():
    num = 10
    def inner():
        # 这种写法无法实现通过闭包修改外部的局部变量'
        nonlocal num
        num = 20
    print('outer函数中的num:', num)  # 10
    inner()  # 执行函数inner,让num=20生效
    print('outer函数中的num:', num)  # 20
    return inner

f = outer()
f()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 闭包的综合示例

闭包的作用:可以在全局作用域中间接访问局部变量(在函数执行以后)

'''
闭包编写三步走:① 有嵌套 ② 有引用 ③ 有返回
分析:
执行f = func()的时候,result赋值为0,然后定义inner,返回inner,最终结果f = inner函数的内存地址
执行f(1),相当于执行inner函数,nonlocal引用局部变量result=0,然后进行+1操作,弹出0+1=1
继续执行
执行f(2),相当于执行inner函数,声明nonlocal result,代表还是引用外部的局部变量,由于此时外部的result已经被
f(1)更改为1了,所以由于局部变量一直没有消失,所以此时result=1,执行+2操作,最终结果为3
'''
def func():
    result = 0
    def inner(num):
        nonlocal result
        result += num
        print(result)
    return inner

f = func()
f(1)  # 1
f(2)  # 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 装饰器

# 什么是装饰器

在不改变现有函数源代码以及函数调用方式的前提下,实现给函数增加额外的功能。

装饰器的本质就是一个闭包函数(三步:① 有嵌套 ② 有引用 ③ 有返回)

有返回代表外部函数返回内部函数的内存地址(内部函数的名称),不带

# 装饰器的雏形

# 要求:把登录功能封装起来(比如封装成一个函数,添加这个登录不能影响现有功能函数)
'''
装饰器:本质是一个闭包,有嵌套、有引用、有返回(返回的是函数的内存地址)
参数fn在check中也是一个局部变量
参数fn:就是要装饰的函数的函数名,如comment,如download
'''
def check(fn):
    def inner():
        # 开发登录功能
        print('登录功能')
        # 调用原函数
        fn()
    return inner


# 评论功能(前提:登录)
def comment():
    print('评论功能')

comment = check(comment)
comment()

# 下载功能(前提:登录)
def download():
    print('下载功能')

download = check(download)
download()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 装饰器定义

'''
装饰器:本质就是一个闭包 ① 有嵌套 ② 有引用 ③ 有返回
'''
def check(fn):
    
    def inner():
        # 开发登录验证功能
        print('验证登录')
        # 执行原有函数
        fn()
    return inner

@check
def comment():
    print('发表评论')

comment()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 装饰器的作用:获取程序的执行时间

'''
定义获取程序的执行时间装饰器 => 闭包(① 有嵌套 ② 有引用 ③ 有返回)
'''
import time

def get_time(fn):
    def inner():
        # ① 添加装饰器修饰功能(获取程序的执行时间)
        begin = time.time()
        # ② 调用fn函数,执行原函数代码
        fn()
        end = time.time()
        print(f'这个函数的执行时间:{end - begin}')
    return inner


@get_time
def demo():
    for i in range(1000000):
        print(i)

demo()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 带有参数装饰器

'''
带有参数的装饰器:① 有嵌套 ② 有引用 ③ 有返回
'''
def logging(fn):
    def inner(*args, **kwargs):
        # 添加装饰器代码(输出日志信息)
        print('-- 日志信息:正在努力计算机 --')
        # 执行要修饰的函数
        fn(*args, **kwargs)  # sum_num(a, b)
    return inner

@logging
def sum_num(*args, **kwargs):
    result = 0
    # *args代表不定长元组参数,args = (10, 20)
    for i in args:
        result += i
    # **kwargs代表不定长字典参数, kwargs = {a:30, b:40}
    for i in kwargs.values():
        result += i
    print(result)

# sum_num带4个参数,而且类型不同,10和20以元组形式传递,a=30,b=40以字典形式传递
sum_num(10, 20, a=30, b=40)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 带有返回装饰器

'''
带有返回值的装饰器:① 有嵌套 ② 有引用 ③ 有返回
如果一个函数执行完毕后,没有return返回值,则默认返回None
'''
def logging(fn):
    def inner(*args, **kwargs):
        print('-- 日志信息:正在努力计算 --')
        return fn(*args, **kwargs)  # fn() = sub_num(20, 10) = result
    return inner

@logging
def sub_num(a, b):
    result = a - b
    return result

print(sub_num(20, 10))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 通用版本的装饰器(以后所有的装饰器以此为准)

'''
通用装饰器:① 有嵌套 ② 有引用 ③ 有返回 ④ 有不定长参数 ⑤ 有return返回值
'''
def logging(fn):
    def inner(*args, **kwargs):
        # 输出装饰器功能
        print('-- 正在努力计算 --')
        # 调用fn函数
        return fn(*args, **kwargs)
    return inner


@logging
def sum_num1(a, b):
    result = a + b
    return result

print(sum_num1(20, 10))

@logging
def sum_num2(a, b, c):
    result = a + b + c
    return result

print(sum_num2(10, 20, 30))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 装饰器高级:使用装饰器传递参数(了解)

基本语法:

def 装饰器(fn):
    ...

@装饰器('参数')
def 函数():
    # 函数代码
1
2
3
4
5
6

实例代码:根据传递参数不同,打印不同的日志信息

'''
通用装饰器:① 有嵌套 ② 有引用 ③ 有返回 ④ 有不定长参数 ⑤ 有return返回值
真正问题:通过装饰器传递参数,我们应该如何接收这个参数呢?
答:在logging方法的外侧在添加一个函数,专门用于接收传递过来的参数
'''

def logging(flag):
    # flag = + 或 flag = -
    def decorator(fn):
        def inner(*args, **kwargs):
            if flag == '+':
                print('-- 日志信息:正在努力进行加法运算 --')
            elif flag == '-':
                print('-- 日志信息:正在努力进行减法运算 --')
            return fn(*args, **kwargs)
        return inner
    return decorator

@logging('+')
def sum_num(a, b):
    result = a + b
    return result

@logging('-')
def sub_num(a, b):
    result = a - b
    return result


print(sum_num(10, 20))
print(sub_num(100, 80))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 扩展:类装饰器(了解)

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

class 类装饰器():
    # 装饰器代码

@类装饰器名称
def 函数():
    # 函数代码
1
2
3
4
5
6

举个栗子:编写一个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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

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

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

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

上次更新: 2025/04/03, 11:07:08
Python进程和线程
Python文件操作

← Python进程和线程 Python文件操作→

最近更新
01
tailwindcss
03-26
02
PaddleSpeech
02-18
03
whisper
02-18
更多文章>
Theme by Vdoing | Copyright © 2019-2025 跨境互联网 | 豫ICP备14016603号-5 | 豫公网安备41090002410995号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式