深浅拷贝、闭包与装饰器
上一期文章给大家介绍了在Python语言中封装、继承和多态三大特征是如何实现的,感兴趣的伙伴可以前往阅读。本期文章主要介绍一下Python语言中深浅拷贝、闭包与装饰器相关的内容,闲言少叙,Let’s go!
一、深浅拷贝
1.1 可变类型和不可变类型
在说深浅拷贝前先说两个概念——什么是可变类型?什么是不可变类型?记住一句话:在不改变地址值的情况下内容可以发生改变的类型为可变类型,反之为不可变类型。
常见数据类型分类如下:
# 可变类型:list、dict、set
# 不可变类型:int、float、str、tuple、bool
1.2 深浅拷贝
-
深拷贝拷贝的多,浅拷贝拷贝的少。
-
深浅拷贝都可以操作可变与不可变类型,但是多数操作可变类型
-
浅拷贝使用copy模块的 copy()函数
-
深拷贝使用copy模块的 deepcopy()函数
代码如下(常做面试题):
# 注意:在使用前先要导包 import copy # 浅拷贝可变类型 # 准备数据 a = [1,2,3] b = [4,5,6] c = [a,b,7,8] d = copy.copy(c) # 操作 a[0] = 11 # 需求1.打印d print(d) # 结果为 [[11,2,3],[4,5,6],7,8] # 浅拷贝不可变类型 # 准备数据 aa = (1,2,3) bb = (4,5,6) cc = (aa,bb) # 操作 dd = copy.copy(cc) # 需求1.打印dd print(cc) # 结果为 ((1,2,3),(4,5,6)) # 深拷贝可变类型 # 准备数据 aaa = [1,2,3] bbb = [4,5,6] ccc = [aaa,bbb,7,8] ddd = copy.deepcopy(ccc) # 需求1.打印ddd print(ddd) # 结果为 [[1,2,3],[4,5,6],7,8] # 操作 aaa[1] = 222 # 需求2.打印ccc print(ccc) # 结果为 [[1,222,3],[4,5,6],7,8] # 需求3.打印ddd print(ddd) # 结果为 [[1,2,3],[4,5,6],7,8] # 深拷贝不可变类型 # 准备数据 aaaa = (1,2,3) bbbb = (4,5,6) cccc = (aaaa,bbbb,7,8) # 操作 dddd = copy.deepcopy(cccc) # 需求1.打印dddd print(dddd) # 结果为 ((1,2,3),(4,5,6),7,8)
二、闭包
闭包是python中的一种独特写法,可以延长函数局部变量的生命周期。要想形成闭包需满足以下三个条件:
-
有嵌套:外部函数内要嵌套 内部函数。
-
有引用:在内部函数中使用 外部函数的变量或者调用外部函数。
-
有返回:在外部函数中, 返回 内部函数名, 即: 内部函数对象。
代码如下:
# 需求:创建一个闭包,实现统计函数执行的次数功能。 # 定义记数函数 def func_count(): # 初始化计数器变量 numCount = 0 # 内部函数 打印内容 记录打印次数 def fn_inner(): # 有嵌套 print('hello world') # 授权 操作外部函数变量 nonlocal numCount numCount += 1 # 有引用 print(f'执行了{numCount}次') return fn_inner # 有返回 if __name__ == '__main__': # 调用 f = func_count() f() f() f()
三、装饰器
装饰器是闭包的一种,目的是在不改变原有函数的基础上, 对其功能做增强或者说扩展。
格式如下:
def 外部函数名(形参列表):
.....
def 内部函数名(形参列表): # 有嵌套
功能扩展 # 有额外功能
...
使用外部函数的变量 # 有引用
return 内部函数名 # 有返回
用法如下:
方式1: 传统用法
变量名 = 装饰器名(被装饰的函数名)
变量名()
方式2: 语法糖, @标记符实现
在要被装饰的函数上, 写: @装饰器名, 之后就跟普通调用函数方式一样, 该怎么调用就怎么调用。
代码如下:
# 定义函数充当装饰器
def check_login(fn_name): # 外部函数 形参列表(固定写法必须有)
def fn_inner(): #内部函数 有嵌套
print('登录中...') # 有扩展(增强\额外功能)
fn_name() #有引用
return fn_inner # 有返回
@check_login # 添加装饰器
# 定义原函数
def comment():
print('发表评论...')
if __name__ == '__main__':
# 1.普通写法
# comment = check_login(comment) # 装饰
# comment() #调用
# 语法糖写法
comment()
# 注意:装饰器的内置函数格式要和被装饰的函数(原函数)格式保持一致。