【python篇】闭包装饰器和拷贝详解
一.闭包
1.1 闭包的定义与作用
1.1.1 闭包定义
闭包:在嵌套函数中,内部函数引用了外部函数的变量 / 参数,且外部函数返回内部函数对象,这个「内部函数 + 它引用的外部变量」的组合,就是闭包。
1.1.2 闭包的作用
让外部函数的局部变量,在外部函数执行完毕后,依然能被内部函数访问、保存,实现数据的持久化和封装。
1.2 闭包的必要条件
1.有嵌套:在函数嵌套 (函数里面再定义函数) 的前提下;
2.有引用:内部函数使用了外部函数的变量 (还包括外部函数的参数);
3.有返回:外部函数返回了内部函数名。
1.3 闭包的格式
def 外部函数名(形参列表):
外部函数的(局部)变量
def 内部函数名(形参列表):
使用外部函数的变量
return 内部函数名
1.4 nonlocal关键字
作用:在嵌套函数(内部函数 / 闭包)中,声明并修改「外层函数的局部变量」,而不是创建新的局部变量。
示例代码:
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
counter = outer()
print(counter())
print(counter())
print(counter())
输出结果:

该代码图解:

global和nonlocal区别:
global:操作全局变量(整个模块顶层的变量)
nonlocal:操作外层嵌套函数的局部变量(闭包专用)
二.装饰器
2.1 作用
在不修改原函数代码、不改变原函数调用方式的前提下,给函数动态添加额外功能。
一个返回函数的外层函数 + 内层包装函数 = 高级闭包(装饰器的本质)
2.2 构成条件
1.有嵌套:在函数嵌套 (函数里面再定义函数) 的前提下;
2.有引用:内部函数使用了外部函数的变量 (还包括外部函数的参数);
3.有返回:外部函数返回了内部函数名;
4.有额外功能:给需要装饰的原有函数增加额外功能。
2.3 格式
def 外部函数名(形参列表):
外部函数的(局部)变量
def 内部函数名(形参列表):
额外拓展的功能
使用外部函数的变量
return 内部函数名
2.4 用法
2.4.1 传统写法
装饰后的函数名 = 装饰器名(原函数名)
装饰后的函数名()
2.4.2 语法糖
原函数上写@装饰器名,之后直接调用原函数
2.5 示例
2.5.1 原函数有(无)参有(无)返回值
import functools
def log_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"[日志] 开始执行 {func.__name__}")
result = func(*args, **kwargs)
print(f"[日志] 结束执行 {func.__name__}")
return result
return wrapper
1.无参无返回
@log_decorator
def hello():
print("Hello World")
hello()
结果:

2.无参有返回
@log_decorator
def get_name():
return "张三"
res = get_name()
print("返回值:", res)
结果:

3. 有参无返回
@log_decorator
def print_info(name, age):
print(f"姓名:{name},年龄:{age}")
print_info("张三", 18)
结果:

4.有参有返回
@log_decorator
def add(a, b):
return a + b
res = add(10, 20)
print("计算结果:", res)
结果:

2.5.2 可变参数
def my_decorator(fn_name):
def fn_inner(*args, **kwargs):
print("...")
return fn_name(*args, **kwargs)
return fn_inner
@my_decorator
def get_sum(*args,**kwargs):
return sum(args) + sum(kwargs.values())
sum = get_sum(1,2,3,x=1,y=2,z=3)
print(sum)
结果:

2.5.3 多个装饰器装饰一个函数
def decorator_A(func):
def wrapper():
print("装饰器A:执行前置操作")
func()
return wrapper
def decorator_B(func):
def wrapper():
print("装饰器B:执行前置操作")
func()
return wrapper
@decorator_B
@decorator_A
def my_func():
print("我是被装饰的核心函数")
my_func()
结果:

多个装饰器装饰一个函数按照由内到外的顺序修饰,效果上是由上向下执行
2.5.4 一个装饰器装饰多个函数
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"=== 开始执行 {func.__name__} ===")
result = func(*args, **kwargs)
print(f"=== 结束执行 {func.__name__} ===\n")
return result
return wrapper
@my_decorator
def add(a, b):
return a + b
@my_decorator
def multiply(a, b):
return a * b
print("结果:", add(1, 2))
print("结果:", multiply(3, 4))
结果:

三. 拷贝
3.1 定义
浅拷贝:通过 copy 模块的 copy.copy() 实现
深拷贝:通过 copy 模块的 copy.deepcopy() 实现
3.2 作用
装饰器会覆盖原函数的元信息(函数名、文档字符串、参数注解等),我们需要通过元信息拷贝还原原函数的特征。
进阶场景会用到浅拷贝 / 深拷贝处理函数参数、对象的引用问题。
3.3 区别
3.3.1 针对可变类型
浅拷贝:仅拷贝第 1 层的可变对象,嵌套的深层可变对象仍然共享原对象的引用(修改深层数据,原对象和拷贝对象会互相影响)
深拷贝:递归拷贝所有层级的可变对象,生成完全独立的新对象(修改任何层级的数据,原对象和拷贝对象都互不干扰)
3.3.2 针对不可变类型
深浅拷贝的效果和普通赋值(=)完全一致
对象创建后,能否在不改变内存地址(id)的前提下,直接修改自身存储的值
能 → 可变类型 list(列表)、dict(字典)、set(集合)
不能 → 不可变类型 int(整数)、float(浮点数)、str(字符串)、tuple(元组)、bool(布尔)、frozenset(冻结集合)
3.4 示例
针对可变类型的浅拷贝和深拷贝
import copy
a = [1, 2, [3, 4]]
shallow = copy.copy(a)
deep = copy.deepcopy(a)
a[2][0] = 0
print("原对象:", a)
print("浅拷贝:", shallow)
print("深拷贝:", deep)
结果:

更多推荐


所有评论(0)