python 浅拷贝和深拷贝

前言

常常在传参, 赋值, 拷贝的时候进入迷乱的状态, 这个xxx之前是这样, 怎么xxx之后变了, 或者, 为什么这个xxx之后还是没有变啊~! (比如遇到类似于下面的题目).于是, 一次次的来回检查代码, 嗯,这代码块没有错, 这也没有错..., 抓狂...表情不好了, 内心纠结不安, 有点想摔鼠标和掀桌子的冲动. 这位少侠, 请等等~~~

# list1 , list2 , list3 分别是多少
def extendList(val, list=[]):
    list.append(val)
    return list


list1 = extendList(1)
print(list1)  # [1]
list2 = extendList(123, [])
print(list2)  # [123]
list3 = extendList('a')
print(list3)  # [1, "a"]
# 原因: list1 处的调用 与 list3 处的调用都共用了默认的参数 list=[] 的引用
# 避免: 使用不可变数据类型, 如 tuple 等
View Code

 

进入正题, 在开始之前, 我们得先了解一下, Python的数据类型分类, 分为可变数据(也称不可哈希 unhashable)和不可变数据(也称可哈希 hashable).

不可变数据类型bool, int, float, str, tuple, complex
可变数据类型list, dict, set

 

一.  对于不可变数据类型的深浅拷贝

 1. 无论怎么操作,你又能奈我何

import copy

def func(f: int):
    return f

a = 10
print('原本,', a, id(a))
f = func(a)
print('传参,', f, id(f))
b = a
print('赋值,', b, id(b))
c = copy.copy(a)
print('浅拷贝,', c, id(c))
d = copy.deepcopy(a)
print('深拷贝,', d, id(d)) 

执行结果

原本, 10 1832873264
传参, 10 1832873264
赋值, 10 1832873264
浅拷贝, 10 1832873264
深拷贝, 10 1832873264

从执行的结果得知,对于不可变数据类型,以上的操作都是相当于贴标签,另起别名,但还是使用同一个地址内容。

2. 且慢,改变原变量的值先

import copy

def func(f: int):
    return f

a = 10
# print('原本,', a, id(a))
f = func(a)
# print('传参,', f, id(f))
b = a
# print('赋值,', b, id(b))
c = copy.copy(a)
# print('浅拷贝,', c, id(c))
d = copy.deepcopy(a)
# print('深拷贝,', d, id(d))

a = 100
print('改变,', a, id(a))
print('传参,', f, id(f))
print('赋值,', b, id(b))
print('浅拷贝,', c, id(c))
print('深拷贝,', d, id(d))

执行结果:

改变, 100 1832876144
传参, 10 1832873264
赋值, 10 1832873264
浅拷贝, 10 1832873264
深拷贝, 10 1832873264

从执行的结果得知,对于不可变的数据类型,原变量的改变,对其他的变量不产生影响。

这种情况,就像大家都来到了同一个房间里,而这个房间存着一个值,同时这个房间可以容纳无数个人,只要来到这个房间就可以拿到这个值。而以上的情况是,a,b,c,d,f都来到了同一个房间,但最后 a 离开了,去了其他的房间,而其他的人还在这个房间里,因此 a 拿到的值和其他人拿到的值不同。

二.  对于可变数据类型的深浅拷贝

对于不可变的数据类型,可以不分什么深浅拷贝,但对于可变的数据类型,总该区别一下了吧

好,那我们来了解一下什么是浅拷贝,什么是深拷贝

浅拷贝将原对象的引用直接赋值给新对象特点:只拷贝一级的元素
深拷贝将原对象的值拷贝给新对象特点:原对象做任何修改都不会改变新对象

 

1. 一波操作之后,且看结果如何

import copy

def func(lst: list):
    return lst

list_a = [1, 2, [3, 4]]
print('原本,', list_a, [id(x) for x in list_a])
list_b = list_a
print('赋值,', list_b, [id(x) for x in list_b])
list_c = copy.copy(list_a)
print('浅拷贝,', list_c, [id(x) for x in list_c])
list_d = copy.deepcopy(list_a)
print('深拷贝,', list_d, [id(x) for x in list_d])
list_f = func(list_a)
print('传参,', list_f, [id(x) for x in list_f])

执行结果:

原本, [1, 2, [3, 4]] [1832872976, 1832873008, 2249777909128]
赋值, [1, 2, [3, 4]] [1832872976, 1832873008, 2249777909128]
浅拷贝, [1, 2, [3, 4]] [1832872976, 1832873008, 2249777909128]
深拷贝, [1, 2, [3, 4]] [1832872976, 1832873008, 2249778057608]
传参, [1, 2, [3, 4]] [1832872976, 1832873008, 2249777909128]

从执行的结果来看,得到的值都一样,但是,深拷贝奇葩,最后的一个地址不同。那么深拷贝到底去做了什么不为人知的事情?

他另外去申请其他的房间(地址),然后将那个房间改造为和原来房间的结构一模一样的房间(值相同)

2. 你把原对象的值改改,让我看看是不是和不可变的数据类型一样

import copy

def func(lst: list):
    return lst

list_a = [1, 2, [3, 4]]
# print('原本,', list_a, [id(x) for x in list_a])
list_b = list_a
# print('赋值,', list_b, [id(x) for x in list_b])
list_c = copy.copy(list_a)
# print('浅拷贝,', list_c, [id(x) for x in list_c])
list_d = copy.deepcopy(list_a)
# print('深拷贝,', list_d, [id(x) for x in list_d])
list_f = func(list_a)
# print('传参,', list_f, [id(x) for x in list_f])

list_a[0] = 5
list_a[2][0] = 6
print('改变,', list_a, [id(x) for x in list_a])
print('赋值,', list_b, [id(x) for x in list_b])
print('浅拷贝,', list_c, [id(x) for x in list_c])
print('深拷贝,', list_d, [id(x) for x in list_d])
print('传参,', list_f, [id(x) for x in list_f])

走......

改变, [5, 2, [6, 4]] [1832873104, 1832873008, 2975057022280]
赋值, [5, 2, [6, 4]] [1832873104, 1832873008, 2975057022280]
浅拷贝, [1, 2, [6, 4]] [1832872976, 1832873008, 2975057022280]
深拷贝, [1, 2, [3, 4]] [1832872976, 1832873008, 2975056935880]
传参, [5, 2, [6, 4]] [1832873104, 1832873008, 2975057022280]

唉...这一波变化还挺大的哈, 那就来看到底是怎么了

(1) 首先, 赋值操作, 会跟随着原对象的值的改变而改变, 完全跟着改变那种

(2) 传参和赋值一样, 一类人

(3) 浅拷贝操作, 不跟着你改变而改变, 但是那些深层次的我可管不了, 因为我拿到的是一个对象, 而不是元素本身

(4) 深拷贝操作, 雷打不动, 不动如山, 我花费那么多的功夫来打造属于自己的地盘, 你说你换你的地盘跟我有一毛钱关系吗

 

总结:

1. 传参传的是引用.

2. 对于不可变数据类型, 不分深浅拷贝.

3. 对于可变数据类型, 浅拷贝只拷贝一级的元素, 其他层级的对象里的元素会随着原对象的同等层级对象的改变而改变; 深拷贝则无论原对象做任何修改, 都不受到影响.

 

转载于:https://www.cnblogs.com/trent-fzq/p/10890851.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值