python 深拷贝和浅拷贝之可变和不可变对象总结

了解深拷贝和浅拷贝之前先要理解可变与不可变对象

python只允许使用引用传递,有可变对象和不可变对象,可变对象:list,dict.
不可变对象有:int,string,float,tuple

Python int,string,float,tuple不可变
举栗子:

def int_object():
i = 89
j = 89
print(id(89))
print(‘i id:‘ + str(id(i)))
print(‘j id:‘ + str(id(j)))
print(i is j)
j = j + 1
print(‘new i id:‘ + str(id(i)))
print(‘new j id:‘ + str(id(j)))
print(i is j)
if __name__ == ‘__main__‘:
int_object()

运行结果
out:?
1715485840
i id:1715485840
j id:1715485840
True
new i id:1715485840
new j id:1715485856
False
有i和j俩个变量的值为89,通过打印89的ID和变量i,j在内存中的id我们得知它们都是指向同一块
内存。所以说i和j都是指向同一个对象的。然后我们修改j的值,让j的值+1.按道理j修改之后应该i
的值也发生改变的,因为它们都是指向的同一块内存,但结果是并没有。因为int类型是不可变类型,
所有其实是j复制了一份到新的内存地址然后+1,然后j又指向了新的地址。所以j的内存id发生了变化。

python list,dict可变对象

举栗子

def dict_object():
a = {}
b = a
print(id(a))
a[‘a‘] = ‘hhh‘
print(‘id a:‘ + str(id(a)))
print(‘a:‘ +str(a))
print(‘id b:‘ + str(id(b)))
print(‘b:‘ + str(b))
if __name__ == ‘__main__‘:
dict_object()

运行结果

65616352
id a:65616352
a:{‘a‘: ‘hhh‘}
id b:65616352
b:{‘a‘: ‘hhh‘}

可以看到a最早的内存地址id是65616352 然后把a赋值给b其实就是让变量b的也指向a所指向的内存
空间。然后我们发现当a发生变化后,b也跟着发生变化了,因为list是可变类型,所以并不会复制
一份再改变,而是直接在a所指向的内存空间修改数据,而b也是指向该内存空间的,自然b也就跟着
改变了。

python函数的参数传递

由于python规定参数传递都是传递引用,也就是传递给函数的是原变量实际所指向的内存空间,
修改的时候就会根据该引用的指向去修改该内存中的内容,所以按道理说我们在函数内改变了传递
过来的参数的值的话,原来外部的变量也应该受到影响。但是上面我们说到了python中有可变类型和
不可变类型,这样的话,当传过来的是可变类型(list,dict)时,我们在函数内部修改就会影响函数
外部的变量。而传入的是不可变类型时在函数内部修改改变量并不会影响函数外部的变量,
因为修改的时候会先复制一份再修改。

举栗子:

def fun1(a_int, b_list):
a_int = a_int + 1
b_list.append(‘6‘)
print(‘inner a_int:‘ + str(a_int))
print(‘inner b_list:‘ + str(b_list))

if __name__ == ‘__main__‘:
a_int = 8
b_list = [10, 11]

fun1(a_int, b_list)

print(‘outer a_int:‘ + str(a_int))
print(‘outer b_list:‘ + str(b_list))

输出结果

inner a_int:9
inner b_list:[10, 11, ‘6‘]
outer a_int:8
outer b_list:[10, 11, ‘6‘]

经过fun1()方法修改后,传递过来的int类型外部变量没有发生改变,而list这种可变类型则因为
fun1()方法的影响导致内容发生了改变。

总结:
python只允许引用传递是为方便内存管理,因为python使用的内存回收机制是计数器回收,
就是每块内存上有一个计数器,表示当前有多少个对象指向该内存。每当一个变量不再使用时,
就让该计数器-1,有新对象指向该内存时就让计数器+1,当计时器为0时,就可以收回这块内存了。
当然它肯定不止用了计数器吧,应该还有其他的技术,比如分代回收什么的等

理解可变与不可变对象之后,现在说浅拷贝与深拷贝

浅拷贝仅仅复制了容器中元素的地址
举栗子:

a = [‘hello‘,[1,2,3]]
b = a[:]
[id(x) for x in a]
[62493024, 62128416]
[id(x) for x in b]
[62493024, 61778560]
a[0]=‘world‘
a[1].append(4)
print(a)
[‘world‘, [1, 2, 3, 4]]
print(b)
[‘hello‘, [1, 2, 3, 4]]

a和b中元素的地址都是相同的,不可变的hello
和可变的list地址都一样,说明浅拷贝只是将容器内的元素的地址复制了一份。这可以通过修改后,
b中字符串没改变,但是list元素随着a相应改变得到验证。

浅拷贝是在另一块地址中创建一个新的变量或容器,但是容器内的元素的地址均是源对象的元素的
地址的拷贝。也就是说新的容器中指向了旧的元素。

(二)
深拷贝,完全拷贝了一个副本,容器内部元素地址都不一样
举栗子:

from copy import deepcopy

a = [‘hello‘,[1,2,3]]
b = deepcopy(a)
[id(x) for x in a]
out: [62493024, 61752504]
[id(x) for x in b]
out: [62493024, 62131936]
a[0] = ‘Name‘
a[1].append(7)
print(a)
[‘Name‘, [1, 2, 4, 7]]
print(b)
[‘hello‘, [1, 2, 4]]

深拷贝后,a和b的地址以及a和b中的元素地址均不同,这是完全拷贝的一个副本,修改a后,
发现b没有发生任何改变,因为b是一个完全的副本,元素地址与a均不同,a修改不影响b。
深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,
仅仅是值相同而已,是完全的副本

时间: 2024-10-10 23:17:47

python 深拷贝和浅拷贝之可变和不可变对象总结的相关文章

python深拷贝和浅拷贝之简单分析

title: python 深拷贝和浅拷贝 tags: python,copy,deepcopy grammar_cjkRuby: true --- python 深拷贝和浅拷贝 python的变量的赋值都是引用 把一个变量赋值给一个变量,不是拷贝这个对象,而是拷贝这个变量的引用 直接赋值 传递的是这个变量的引用 浅拷贝 拷贝的是这个变量的引用,会产生新的对象 浅拷贝会产生一个新的对象,但是浅拷贝的内容还是原有对象的引用 看下面的例子 浅拷贝 import copy a = [1, 2, 3,

完全理解python深拷贝和浅拷贝

import copya = [1, 2, 3, 4, ['a', 'b']]  #原始对象b = a  #赋值,传对象的引用c = copy.copy(a)  #对象拷贝,浅拷贝d = copy.deepcopy(a)  #对象拷贝,深拷贝a.append(5)  #修改对象aa[4].append('c')  #修改对象a中的['a', 'b']数组对象print 'a = ', aprint 'b = ', bprint 'c = ', cprint 'd = ', d 输出结果:a = 

python 深拷贝与浅拷贝

浅拷贝的方式有: lst=[1,2,3] (1)直接赋值: lst_cp = lst (2)for循环遍历生成:lst_cp= [i for i in lst] (3)copy模块下,copy.copy仍为浅拷贝 深拷贝的方式 (1)借助copy模块 >>> import copy >>> lst_cp = copy.deepcopy(lst) 以上方法的测试: 注意:因为string类型是不可变类型,所以修改string元素时会新创建一个地址空间放置数据 (1)直接赋

Python 深拷贝和浅拷贝

Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果. 下面本文就通过简单的例子介绍一下这些概念之间的差别. 对象赋值 直接看一段代码: will=["Will",28,["Python","C#","JavaScript"]] wilber=will print id(will) print will print [id(x) for x in will] print id

图解Python深拷贝和浅拷贝

Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果. 下面本文就通过简单的例子介绍一下这些概念之间的差别. 对象赋值 直接看一段代码: will = ["Will", 28, ["Python", "C#", "JavaScript"]] wilber = will print id(will) print will print [id(ele) for ele in wi

【转载】图解 Python 深拷贝和浅拷贝

作者:田小计划 出处:http://www.cnblogs.com/wilber2013/ Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果. 下面本文就通过简单的例子介绍一下这些概念之间的差别. 对象赋值 直接看一段代码: will = ["Will", 28, ["Python", "C#", "JavaScript"]] wilber = will print id

Python 深拷贝、浅拷贝

Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果. 首先,对赋值操作我们要有以下认识: 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 ). 修改不可变对象(str.tuple)需要开辟新的空间 修改可变对象(list等)不需要开辟新的空间 浅拷贝 ? 浅拷贝仅仅复制了容器中元素的地址,就是拷贝了引用,并没有拷贝内容. In [1]: a = [11,22,33] In [2]: id(a) Out[2]: 22132

Python 深拷贝 vs 浅拷贝

在一些业务场景中, 有时候我们需要复制一个对象, 但是又不想对原来的对象产生影响, 就想搞个 副本 来为所欲为地操作嘛. 但是呢, 在 Python中呢, 又不能通过 赋值 的方式达到效果, 为啥呢? 被坑过几次就明白了, 这里面蕴含有很多的学问呀, 涉及 Python 变量的本质, 可变与不变对象, 深浅拷贝问题... 是得来总结一波了. 副本危机 之前在做数据分析的时候, 想把一个 DataFrame 对象, 引用的是一张Excel表数据, 我当时想拷贝一个副本来瞎几把操作, 而想着不会改变

python深拷贝与浅拷贝

浅拷贝:b=copy.copy(a),对引用的拷贝,只拷贝父对象,所以id(b)不等于id(a),但对象内部资源依然引用,内部id(b[0])等于id(a[0]),id(b[4])等于id(a[4]) 深拷贝:c=copy.deepcopy(a),对对象资源的拷贝,id(c)不等于id(a),内部id(c[0])等于id(a[0]),id(c[4])不等于id(a[4]) 注意:因为数字是不可变量,所以数字的id不变,无论深拷贝还是浅拷贝,id(b[0]).id(c[0])都等于id(a[0])