2018-03-31-我复制的变量又双叒叕自己改变了

2018-04-01  本文已影响0人  0xC00005

前言

还在傻傻的用等号复制变量嘛?a=b?
惊奇,发现改变a的值b的值有时候改变了有时候却不改变qwq
原地爆炸如下:

>>> a=123
>>> b=a
>>> b
123
>>> a
123
>>> b+=100
>>> b
223
>>> a
123
>>> c=[1,2,3]
>>> d=c
>>> d.append(5)
>>> d
[1, 2, 3, 5]
>>> c
[1, 2, 3, 5]
>>> ???????突然智障

一股子智障的气息铺面而来

正文

百度科普了一下复制相关的使用姿势,get到新动作copy库里面的copy和deepcopy【???】
然后又一次入了内存的坑
我以前用的等号复制【我发明的名字】不仅仅会复制对象的值本身,或者说等号复制并不是创建一个新的对象,而是让一个新对象的值指向原来你复制的对象,这样就很有意思了,因为内存地址是唯一的,如果你改变了之前内存地址的值,那么新的对象也就会改变了
这里科普一下一个去Python内存地址的新姿势id(var),使用这个函数可以去任意变量的内存地址
首先我们定义个int类的变量,然后复制并改变:

>>> var = 123
>>> copy_var = var
>>> copy_var ==var #这里证明两个变量的值相等
True
>>> copy_var is var #这里证明两个变量的内存地址相等
True
>>> uncopy_var = 123 #重新定义一个变量,发现结果一样
>>> uncopy_var == var
True
>>> uncopy_var is var
True

这里可能需要瞎扯一下变量与内存的关系【又来了】
因为这里的三个变量都是同样的一个值【这里强调一个单独的变量而不是类似于集合之类的】,Python为了节省内存空间,所以只会开辟一个内存空间......然后把所有同样的变量都指向一个内存地址

>>> print(id(var))
1570861024
>>> print(id(copy_var))
1570861024
>>> print(id(uncopy_var))
1570861024

但是如果我改变其中的某一个值的话

>>> copy_var+=100
>>> copy_var is var
False
>>> print(id(copy_var))
1570864224
>>> print(id(var))      #这里可以很明显的看到加上100内存地址也是一样的加上100
1570861024

我都有点开始怀疑是不是Python专门用这段的内存地址来存储数字来着
【而不是像某些跑得很快的语言是随意分配的】
吓得我重新开了一遍环境:

Python 3.6.3 |Anaconda, Inc.| (default, Oct 15 2017, 03:27:45) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> var = 123
>>> print(id(var))
1570861024

妈耶还真是一样的【开始慌张】
那么集合呢,比如列表

>>> lst=[1,2,3,4]
>>> copy_lst = lst
>>> copy_lst == lst
True
>>> copy_lst is lst
True

现在我改变其中某一些的值。比如我对原来的lst执行压入操作

>>> copy_lst
[1, 2, 3, 4]
>>> lst.append(5)
>>> copy_lst
[1, 2, 3, 4, 5]
>>> lst
[1, 2, 3, 4, 5]

这又是为什么呢【突然智障】
一种解释的方法就是因为列表是多个元素的集合,元素的内存值是不变的,但是这里因为copy_lst的内存地址=lst,所以只要lst改变了copy_lst也会改变,

>>> id(lst[0]) == id(copy_lst[0])#元素内存地址相同
True
>>> id(lst) == id(copy_lst)#列表内存地址相同
True

或者还有另外一种说法,请看下面神奇的微操:

>>> l=[] #这里是一个空列表
>>> c=l #这里我们使用等号把l的值和内存地址复制给c
>>> c == l #都是空列表,所以返回TRUE
True
>>> c is l #等号复制内存地址相同 ,也返回TRUE
True
>>> d =[] #新建一个空列表d
>>> c is d #每一个**新建**的列表的内存地址都是不一样的
False 
>>> c == d #但是都是空列表所以值相同
True

现在导入我们的copy模块

copy.copy 可能是藕断丝连的复制

附:您可能需要导入【woc这么重要的模块都不是内置模块嘛】
我先复制了上面lst的值

import copy
>>> copy.copy_lst=copy.copy(lst)
>>> id(copy.copy_lst) == id(lst)
False

妙哉,但你以为这就完事大吉了?
如果我在列表里面存列表呢?

>>> a=[1,2,[3,4]]  #第三个值为列表[3,4],即内部元素
>>> d=copy.copy(a) #浅拷贝a中的[3,4]内部元素的引用,非内部元素对象的本身
>>> id(a)==id(d)
False
>>> id(a[2])==id(d[2])
True
>>> a[2][0]=3333  #改变a中内部原属列表中的第一个值
>>> d             #这时d中的列表元素也会被改变
[1, 2, [3333, 4]]

惊不惊喜意不意外?
这时候就需要我们的:

copy.deep♂copy 有点黑暗的名字

档然了我们食用的时候请去掉男魂标志
这个方法相比较于之前的copy可不是只是有点黑♂暗而已
他所复制出来的所有的值都是不一样的,相当于重新开辟了一段内存空间来存储

>>> e=copy.deepcopy(a) #e为深拷贝了a
>>> a[2][0]=333 #改变a中内部元素列表第一个的值
>>> e
[1, 2, [3333, 4]] #因为时深拷贝,这时e中内部元素[]列表的值不会因为a中的值改变而改变

最后

参考资料:
copy & deepcopy 浅复制 & 深复制

最后的最后

我求求你们mark一下吧www

上一篇下一篇

猜你喜欢

热点阅读