【Python闭包装饰器与深浅拷贝】

目录

1 闭包

2 装饰器

2.1 什么是装饰器

2.2 装饰器的定义

2.3 四种类型函数的装饰器

2.4 通用装饰器

3 深浅拷贝

3.1 浅拷贝:

3.2 深拷贝:

总结:


1 闭包

问:

当调用完函数后,函数内定义的变量就销毁了,但有时需要保存函数内的这个变量,并在这个变量的基础上完成一系列的操作

 比如: 每次在这个变量的基础上和其它数字进行求和计算,那怎么办呢?

注:闭包可以保存函数内的变量,不会随着调用完函数而销毁

闭包的构成条件:有嵌套、有引用、有返回

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

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

f = func()  # 相当于把inner在内存中的地址0x7fbc9b3f8e18赋值给变量f
f()  # 找到inner函数的内存地址,并执行器内部的代码(num=20),在于闭包函数保留了num=20这个局部变量

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

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

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

2 装饰器

2.1 什么是装饰器

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

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

2.2 装饰器的定义

方式1: 传统方式变量名 = 装饰器名(原有函数)

                                      变量名()

方式2: 语法糖: @装饰器名   (这种方式最常见也最常用)  

例方式2,发表评论前,都是需要先登录的。

先定义有发表评论的功能函数,然后在不改变原有函数的基础上,需要提示用户要先登录

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

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

comment()

装饰器的构成条件:

有嵌套在函数嵌套(函数里面再定义函数)的前提下;

有引用内部函数使用了外部函数的变量(还包括外部函数的参数)

有返回外部函数返回了内部函数名;

有额外功能给需要装饰的原有函数增加额外功能

装饰器的作用:

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

装饰器本质上就是一个闭包函数

2.3 四种类型函数的装饰器

1.无参无返回值:

以下例子同时使用了方法1和方法2.所以相当于装饰了两次,所以打印两次。

# 装饰器: 在不改变原有函数(定义和调用)基础上,额外增加新的功能
# 3.需求: 在原有函数计算出结果之前,先提示用户:正在计算中...
# 有嵌套
def outter(func):
    def inner():
        print("正在计算中...")
        func()
    return inner
# 1.定义原有函数
@outter  # 4.语法糖方式装饰器开始装饰原有函数get_sum
def get_sum():
    a = 1
    b = 2
    print(a + b)
# 4.1原始方式装饰器开始装饰原有函数get_sum
get_sum = outter(get_sum)
# 2.再调用原有函数
get_sum()

2.有参无返回值:

# 3.需求: 再原有函数出结果之前,输出: 正在计算中...
# 有嵌套
def oueter(func):
    def inner(a, b):
        # 有额外功能
        print("正在计算中...")
        # 有引用 TODO 此处func就是原有函数
        func(a, b)

    # 有返回
    return inner
# 4.语法糖方式装饰原有函数
@oueter
# 1.定义原有函数
def func2(a, b):
    sum = a + b
    print(sum)
# 2.调用原有函数
# TODO 此时func2就是inner内部函数
func2(3, 4)

3.无参有返回值:

# 3.需求: 再原有函数出结果之前,输出: 正在计算中...
# 有嵌套
def oueter(func):
    def inner():
        # 有额外功能
        print("正在计算中...")
        # 有引用 TODO 此处func就是原有函数
        return func()
    # 有返回
    return inner
# 4.语法糖方式装饰原有函数
@oueter
# 1.定义原有函数
def func2():
    sum = 1 + 2
    return sum
# 2.调用原有函数
# TODO 此时func2就是inner内部函数
print(func2())
#正在计算中...
#3

4.有参有返回值:

# 3.需求: 再原有函数出结果之前,输出: 正在计算中...
# 有嵌套
def oueter(func):
    def inner(a, b):
        # 有额外功能
        print("正在计算中...")
        # 有引用 TODO 此处func就是原有函数
        return func(a ,b)
    # 有返回
    return inner
# 4.语法糖方式装饰原有函数
@oueter
# 1.定义 TODO 原有函数
def func2(a, b):
    sum = a + b
    return sum
# 2.调用原有函数
# TODO 此时func2就是inner内部函数
print(func2(10, 20))
#正在计算中...
#30

2.4 通用装饰器

# 3.需求: 在原有函数计算出结果之前,先提示用户:正在计算中...
# 有嵌套
def outter(func):
    def inner(*args, **kwargs):
        print("正在计算中...")
        # 有引用 TODO: 此处func就是原有函数
        data = func(*args, **kwargs)
        return data
    return inner
# 4.语法糖方式添加装饰器
@outter
# 1.先定义函数
# 功能是计算多个数的和,要求位置传参和关键字传参均可
def get_sum(*args, **kwargs):
    sum = 0
    for i in args:
        sum += i
    for i in kwargs.values():
        sum += i
    return sum
# 2.再调用函数
# TODO 此时get_sum就是inner内部函数
he = get_sum(1, 2, 3, 4, 5, a=6, b=7)
print(he) #28

 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,

       内到外的装饰过程

3 深浅拷贝

几个概念:

- 变量:是一个系统表的元素,拥有指向对象的连接空间
- 对象:被分配的一块内存,存储其所代表的值
- 引用:是自动形成的从变量到对象的指针
- 类型:属于对象,而非变量
- 不可变对象:一旦创建就不可修改的对象,包括数值类型、字符串、布尔类型、元组

*(该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。)*

- 可变对象:可以修改的对象,包括列表、字典、集合

*(该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。)*

当我们写到a = "python":

Python解释器干的事情:

① 创建变量a

② 创建一个对象(分配一块内存),来存储值 'python'

③ 将变量与对象,通过指针连接起来,从变量到对象的连接称之为引用(变量引用对象)

3.1 浅拷贝:

浅拷贝: 创建新对象,其内容是原对象的引用

浅拷贝之所以称为浅拷贝,是它仅仅只拷贝了一层,拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已

可变类型浅拷贝:

不可变类型浅拷贝:

3.2 深拷贝:

深拷贝:和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。只有一种形式,copy模块中的deepcopy函数。

可变类型深拷贝:

不可变类型深拷贝:

总结:

浅copy跟深copy的区别:

对于不可变类型,浅copy跟深copy都copy不成功,相当于引用,新的对象与原对象共享内存中的子对象。

对于可变类型,对于第一层数据浅copy跟深copy都可以copy成功,从内存重新开辟一段空间给新的对象。但是对于第二层数据,比如列表里边套列表,列表里边套集合,列表里边套字典等,浅copy不成功,深copy会把第二层数据也会开辟一段空间。以下举例:

# 从copy模块导入浅拷贝功能
from copy import copy
# 从copy模块导入深拷贝功能
from copy import deepcopy

# 可变类型: 列表  列表嵌套列表或者集合或者字典
a = [1, 2, 3, [4, 5, [True, False]]]
b = copy(a)
c = deepcopy(a)
# 1层情况下: 深浅拷贝都成功
print('a:', a, 'b:', b, 'c:', c)
print('a:', id(a), 'b:', id(b), 'c:', id(c))
# 2层及以上: 浅拷贝只是引用传递,深拷贝是完全拷贝,深拷贝都开辟了新的空间地址,以后相互不受影响
print(a[3], b[3], c[3])
print(id(a[3]), id(b[3]), id(c[3]))
print(a[3][2], b[3][2], c[3][2])
print(id(a[3][2]), id(b[3][2]), id(c[3][2]))

结果:

deepcopy的深度要比copy更高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值