闭包和装饰器与深浅拷贝
闭包
- 定义 :内部函数使用了外部函数的变量
- 作用 :用于延长函数内变量的生命周期 ,或编写装饰器
- 语法 :
# 外部函数
def 外部函数名(外部参数):
外部函数(局部)的变量
# 内部函数
def 内部函数名(内部参数):
...[使用外部函数的变量]
return 内部函数名 # 外部函数的返回值
构成条件 :
- 有嵌套: 在函数嵌套(函数里面再定义函数)的前提下
- 有引用: 内部函数使用了外部函数的变量(还包括外部函数的参数)
- 有返回: 外部函数返回了内部函数名
细节 :函数名 和 函数名() 是两个概念,前者表示函数对象,后者表示调用函数获取返回值
函数名可以赋值给变量,这个变量就是函数对象
例子:
def fun_outer(num1):
def fun_inner(num2):
sum = num1 + num2
print(f"两个数的和为{sum}")
return fun_inner
# 调用
fun = fun_outer(10)
fun(20) # 输出: 两个数的和为30
fun_outer(10)(20) #另一种调用方法
-
nonlocal关键字
-
声明能够让内部函数去修改外部函数的变量
def outer(): num1 = 10 def inner(num2): nonlocal num1 # 声明num1为nonlocal变量 num1 += num2 print(f"两个数的和为{num1}") return inner fun = outer() fun(20) # 输出: 两个数的和为30 fun(20) # 输出: 50
装饰器
- 在不改变原有函数的基础上,对其增加额外功能
-
装饰器本质上就是一个闭包函数
-
构成条件 :
- 有嵌套: 在函数嵌套(函数里面再定义函数)的前提下
- 有引用: 内部函数使用了外部函数的变量(还包括外部函数的参数)
- 有返回: 外部函数返回了内部函数名
- 有额外功能:给需要装饰的原有函数增加额外功能
-
定义语法 :
def check(fn_name): #fn_name是一个函数名
def fn_inner(): #定义内部函数
print("检查用户是否登录") #添加额外功能
fn_name()
return fn_inner #返回内部函数名
- 调用语法:
# 格式1:传统写法
装饰后的函数名 = 装饰器名(被装饰的原函数名)
装饰后的函数名()
# 格式2:语法糖
#在要被装饰的原函数前,直接写 @装饰器名,之后直接调用函数即可
--------------------------------
#传统方式
comment = check(comment)
comment()
# 语法糖
@check
def pay():
print("充值中...")
pay()
-
多次修饰:一个函数可以挂任意多个装饰器,一般装饰自由内到外、 执行自上而下(语法糖)
-
装饰器的使用场景 :有(无)参有(无)返回值的原函数装饰、不定长参数原函数装饰
-
注意 :装饰器的内部函数格式要和被装饰的原函数保持一致
# 可变函数的装饰器用法
def my_decorator(func):
def inner(*args,**kwargs):
print("正在执行求和操作")
result = func(*args, **kwargs)
return result
return inner
@my_decorator
def get_sum(*args, **kwargs):
"""
用于计算 数字元组 和 字典中数字的和
:param *args: 数字元组,接收任意数量的位置参数 -> 元组
:param **kwargs: 字典,key:字符串,value:数字,接收关键字参数 -> 字典
"""
sum = 0
for i in args:
sum += i
for v in kwargs.values():
sum += v
return sum
sum = get_sum(1,3,4,5,a=3,b=3) # 输出: 正在执行函数前的操作
print(f"数字元组和字典中数字的和为{sum}") # 输出: 数字元组和字典中数字的和为16
- 装饰器含参时,常是嵌套装饰器.装饰器(本身)的参数有且只能有一个(即函数名,也可把函数名和参数封装成元组传入)
- 也可通过
函数名.__name__ == "函数名"的方法判断函数
# 定义含参装饰器(就是嵌套装饰器)
def logging(flag): #装饰器的参数,可以有多个参数
def my_decorator(fun_name):
def fun_inner(a,b):
if flag == "+":
print("正在计算[加法]中...")
elif flag == "-":
print("正在计算[减法]中...")
return fun_name(a,b)
return fun_inner
return my_decorator
# 定义函数
@logging("+")
def jiafa(a,b):
return (a+b)
@logging("-")
def jianfa(a,b):
return (a-b)
深浅拷贝
-
变量类型
- 不可变类型: 字符串,元组,整形,浮点型
- 可变类型: 字典,列表,集合(可变类型中内容的修改可能同时影响新旧变量)
-
变量底层:
- 每个定量在内存中有固定地址存储,不可变类型变量的赋值操作就是把对应的值的地址给予变量,所以值不可修改(只能重新给变量赋值,例如原
a=(1,2)重赋为a=(11,22)) - 可变类型变量的赋值操作是重新开辟一段内存存储,所以值可修改(能在不重新赋值的前提下,列如
list[1]=10)
- 每个定量在内存中有固定地址存储,不可变类型变量的赋值操作就是把对应的值的地址给予变量,所以值不可修改(只能重新给变量赋值,例如原
-
概述:
-
所谓的深浅拷贝分别指的是
copy模块的deepcopy()和copy() -
深拷贝拷贝的多,浅拷贝拷贝的少
-
深浅拷贝主要是针对于可变类型来说的,深拷贝拷贝所有层(可变),浅拷贝只拷贝第1层(可变)
-
如果是针对于不可变类型,则用法和普通赋值一样,并无区别
-
import copy #导包
def dm01_普通赋值():
# 普通赋值之 不可变类型
a = 10
b = a
print('id(a)->', id(a)) # 0x01
print('id(b)->', id(b)) # 0x01
print('id(10)->', id(10)) # 0x01
# 修改一个变量值只会对那个变量单独产生影响
a = 20
print('id(10)->', id(20)) #0x03
print('id(a)->', id(a)) #0x03
print('id(b)->', id(b)) #0x01
print(a) #20
print(b) #10
# 原理:每个不可变变量的值都有固定的地址,对变量的赋值就是把这个变量指向对应值的地址
# 普通赋值之 可变类型
a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]
d = c
print('id(c)->', id(c)) #0x02
print('id(d)->', id(d)) #0x02
# 此时不论修改c还是d,两者都会变化.且地址不变
c[1] = [2,41,5]
print('id(c)->', id(c)) #0x02
print('id(d)->', id(d)) #0x02
print(c) #[[1, 2, 3], [2, 41, 5]]
print(d) #[[1, 2, 3], [2, 41, 5]]
# 普通赋值就是把变量的地址给新变量(类似于引用)
#原理:可变变量是内存中新开辟的地址
def dm02_浅拷贝可变类型():
a = [1, 2, 3] #0x01
b = [11, 22, 33] #0x02
c = [6, 7, a, b] #0x03
# 浅拷贝可变类型,开辟一份新内存真实拷贝一份(只拷贝第一层,有变量就拷贝地址)
d = copy.copy(c)
print('id(c)->', id(c)) #0x03
print('id(d)->', id(d)) #0x04
# 测试2
print(id(c[2])) #0x01
print(id(a)) #0x01
# 修改
a[2] = 22
c[1] = 10
print('c->', c) #[6,10,[1,2,22],[11,22,33]]
print('d->', d) #[6,7,[1,2,22],[11,22,33]]
# 浅拷贝只拷贝一层[6,7,a的地址,b的地址]
#原理:拷贝到地址,修改(指向地址的变量)就会对原变量产生影响.
def dm03_浅拷贝不可变类型():
# 不可变类型 a b c
a = (1, 2, 3)
b = (11, 22, 33)
c = (6, 7, a, b) #0x03
# 浅拷贝不可变类型就是把两个变量名指向同一地址,不开辟内存
d = copy.copy(c)
print('id(c)->', id(c)) #0x03
print('id(d)->', id(d)) #0x03
#只能重新赋值,不能修改
def dm04_深拷贝可变类型():
a = [1, 2, 3] #0x01
b = [11, 22, 33] #0x02
c = [6, 7, a, b] #0x03
# 深拷贝,开辟一份新的内存.连结构也拷贝
d = copy.deepcopy(c)
print('id(c)->', id(c)) #0x03->[6,7,0x01,0x02]
print('id(d)->', id(d)) #0x04->[6,7,0x04,0x05]
# 深拷贝直接拷贝所有层(不拷贝地址)
a[1] = 100
b[1] = 800
print(f'c: {c}') #[6,7,[1,100,3],[11,800,33]]
print(f'd: {d}') #[6,7,[1,2,3],[11,22,33]]
def dm05_深拷贝不可变类型():
a = (1, 2, 3) #0x01
b = (11, 22, 33) #0x02
c = (a, b) #0x03
#深拷贝不可变类型
d = copy.deepcopy(c)
print(id(c)) #0x03
print(id(d)) #0x03
#同赋值
- 总结:
- 不可变类型同赋值,不开辟空间,不能更改内容(只能重新赋值).
- 可变类型赋值时耦合性最高,浅拷贝多层才耦合,深拷贝不耦合.