Python_赋值和深浅copy

Python中赋值和深浅copy存储变化    

  在刚学python的过程中,新手会对python中赋值、深浅copy的结果感到丈二和尚。经过多次在网上查找这几个过程的解释说明以及实现操作观察,初步对这几个过程有了浅显的认识。以下内容仅是我在学习过程中遇到的问题,然后上网搜验证,最后理解。博文也许没有将这部分内容写明白,也许有不对的地方,如果有大佬看到希望能指点一下新人。随着后面的学习以及理解会再次补充此内容。

  • id函数

  id方法的返回值就是对象的内存地址

  • 执行赋值语句

  在python中执行一条赋值语句,python解释器做了这两件事情:

  1.在内存中创建一个字符串;

  2.在内存中创建一个变量,并把字符串的内存地址赋值给变量;

1 >>> a=‘haizei‘
2 >>> id(a)
3 1895098520440    #字符串‘haizei’的内存地址

  python解释器在内存中创建一个字符串‘haizei’,创建一个指针a,并把字符串的内存地址保存在指针中。指针就指向了字符串,访问指针a就是访问字符串‘haizei’。

  • 赋值a赋值给变量b
1 >>> a=‘haizei‘
2 >>> id(a)
3 1895098520440
4 >>> b=a
5 >>> id(b)
6 1895098520440    #a和b的指向的值的内存地址是相同的,说明赋值是把变量a指向的值的内存地址赋值给b,使b和a一样也指向同一个内存地址。b和a都是存储的值的内存地址

  执行b=a,创建了一个变量b,并把b指向a指向的字符串的内存地址。

  执行b=a,创建一个指针b,并把存储在指针a中的内存地址赋值给指针b,这样指针b就也指向了a指向的字符串。赋值完成后b就和a没有任何关系了,只和它指向的字符串有关系。

  • 重新给a赋值
 1 >>> a=‘haizei‘
 2 >>> id(a)
 3 1895098520440
 4 >>> b=a
 5 >>> id(b)
 6 1895098520440
 7 >>> a=‘lufei‘   #python解释器在内存中新开辟一块空间创建字符串‘lufei’,并把a重新指向字符串‘abc’
 8 >>> id(a)
 9 1895098520496
10 >>> id(b)
11 1895098520440   #变量b指向的还是原来的字符串

  前面给变量a赋值,把a的值赋给b,结果是在内存中创建了一个值,a和b都指向了这个值。现在重新给a赋一个新的值,执行一条赋值语句,过程是在内存中创建一个新的值,然后把新值的内存地址保存在变量指针a中。结果是内存中存在两个值b指向原来的值,a指向新创建的值。以上结果说明对于赋值操作,如果是将另一个变量的值赋给一个变量实际上是将另一个变量存储的值的内存地址同样保存在被赋予值的变量中;如果是给变量赋一个新的值,那么相当于是一个初始化操作会在内存中开辟一段空间创建一个值,将值的内存地址保存在被赋值的变量指针中。

  • 创建复杂数据结构
1 >>> name=[‘lufei‘,‘suolong‘,‘namei‘]
2 >>> id(name)
3 1895098506504
4 >>> id(name[0])
5 1895098520496
6 >>> id(name[1])
7 1895098520608
8 >>> id(name[2])
9 1895098520664

  创建的列表的名称为name,从代码结果可以看到:列表name指向的整体内存地址和列表中每个元素指向的地址都不相同。说明对于复杂的数据结构来说,里面存储的也只是一个个值的内存地址。从下图可以看出来对于元组、列表、字典这样的复杂的数据结构,在内存中其变量和值是分开的,每个元素都对应一个变量指针,变量指针指向值的位置。变量和值的存储是分开的,把整个数据结构赋值给另一个变量的执行过程是在内存中创建相同数量的一些变量,然后把原数据结构中存储在变量中的值的地址赋值给新创建的变量。结果如右图。

  

  对列表中的元素的增删改查操作不会影响到列表本身的内存地址,改变的是列表内的某个元素的地址引用;如果是重新初始化一个列表那么修改的是列表名称指向的整个列表的内存地址。可当我们给name这个列表重新初始化赋值时,就给name这个变量重新赋予了一个新的内存地址,覆盖了原来的列表的地址。

 1 >>> name=[‘lufei‘,‘suolong‘,‘namei‘]
 2 >>> id(name)
 3 1895098506504
 4 >>> id(name[0])
 5 1895098520496
 6 >>> id(name[1])
 7 1895098520608
 8 >>> id(name[2])
 9 1895098520664
10 >>> name[0]=‘zhang‘    #重新给序号为0的元素赋值
11 >>> id(name)           #整个列表的内存地址没有改变
12 1895098506504
13 >>> id(name[0])
14 1895098520552          #序号为0的元素的内存地址发生改变
15 >>> name=[‘zhao‘,‘qian‘,‘sun‘]        #重新初始化赋值name这个变量
16 >>> id(name)
17 1895098512136                         #name这个变量指向的内存地址发生了改变

  这里重新给name变量赋值,赋一个新的列表给name。整个过程以及结果和上面的单个变量的赋值是类似的,不同的是这里是一个列表是多个单个元素(变量)的集合。相当于是对多个变量做这样的操作。

  • 浅copy
 1 >>> name={‘lufei‘:‘cap‘,‘haizei‘:[‘suolong‘,‘namei‘,‘qiaoba‘]}   #1、创建字典name
 2 >>> a=name.copy()           #执行copy方法,吧结果赋值给字典a
 3 >>> id(name)
 4 1895098004488
 5 >>> id(a)
 6 1895098007368               #从内存地址上看原来的name的内存地址和a的内存地址不相同,说明copy是创建了一个name的副本,把副本的内存地址保存在变量a中
 7 >>> id(name[‘lufei‘])
 8 1895098521056
 9 >>> id(a[‘lufei‘])
10 1895098521056               #分别查看原字典name和副本字典a中键‘lufei’的值的内存地址,地址相同。说明这里存储的是同一个值
11 >>> id(name[‘haizei‘])
12 1895098506504
13 >>> id(a[‘haizei‘])
14 1895098506504               #同样副本中另一个键的值的内存地址也是相同的,说明两个字典内的键都指向相同的值
15 >>> name[‘lufei‘]=‘haha‘    #执行赋值语句修改name字典中键‘lufei’的值
16 >>> name[‘haizei‘].remove(‘namei‘)       #调用字典name中‘haizei’键的值,并调用值的remove方法移除列表中的‘namei’元素
17 >>> name
18 {‘haizei‘: [‘suolong‘, ‘qiaoba‘], ‘lufei‘: ‘haha‘}
19 >>> a
20 {‘haizei‘: [‘suolong‘, ‘qiaoba‘], ‘lufei‘: ‘cap‘}     #分别查看字典name和a,发现对字典name执行赋值语句时,副本没有跟着修改,对键‘haizei’做的修改副本跟着做了修改?
21 >>> id(name)
22 1895098004488
23 >>> id(a)
24 1895098007368        #分别查看两个字典的内存地址,和修改之前的内存地址是一样的。说明修改复杂的数据结构中元素的值不影响变量对整体数据结构内存地址的指向(数据结构的内存地址没有改变)
25 >>> id(name[‘lufei‘])
26 1895098520944
27 >>> id(a[‘lufei‘])
28 1895098521056        #分别查看两个字典中键‘lufei’的值的内存地址,发现name中该键的值被修改了其内存地址改变了。而字典a的键的值没有改变,对应的该键的值的内存地址没有改变。
29 >>> id(name[‘haizei‘])
30 1895098506504
31 >>> id(a[‘haizei‘])
32 1895098506504        #对字典name中键‘haizei’的值做了修改,但是查看该键的值的内存地址时,两个字典都没有变化,和修改前是一样的?

  在理解深浅copy的时候,先用一张图来表示字典在内存中的存在方式,再说明深浅copy分别copy的是字典的那些部分。当对原字典或者副本进行修改时,修改的部分在 内存中造成什么变化,是否对原字典和副本字典产生了影响。当理解了这些的时候也就理解了深浅copy

  这是我们在内存中创建的字典的存在形式。可以这么理解,字典name保存整体字典的内存地址从而指向这个字典,字典内包含两个键,键1保存的是一个值的内存地址从而指向这个值,键2保存的是一个列表的整体地址从而指向这个列表。列表内又有三个指针分别保存三个值的内存地址作为列表的三个元素。

  浅copy复制的是哪部分?浅copy仅复制了图中的字典变量的部分。也就是复制了键1下保存的内存地址和键而的内存地址

  当修原字典或者副本字典的键1关联的值时,在内存中开辟一片新的空间存储一个新的值,并把新创建的值的内存地址保存到被修改的字典的对应键的指针里。因此被修改的字典的键指向了新创建的值,未被修改的字典中相同的键指向的还是原来的值,所以当修改的键对应的值是一个简单的数据类型的值时,修改后原字典和副本字典的值出现了不同。

  当修改的键对应的值是一个数据结构时,由于原字典中键2中仅保存了列表的整体地址,副本字典中copy的时候,键2也仅仅是copy的整个列表的内存地址。原字典和副本字典都不清楚列表中值的内存地址是多少,只是通过列表的整体地址可以访问到列表中的值。在这种情况下列表中的值发生变化时就对原字典和副本字典都产生了影响。

  • 深copy
 1 >>> from copy import deepcopy         #使用深copy时需要从copy模块中导入deepcopy
 2 >>> name={‘captain‘:‘lufei‘,‘sailor‘:[‘suolong‘,‘namei‘,‘qiaoba‘]}
 3 >>> copy_name=deepcopy(name)          #执行深copy
 4 >>> id(name)
 5 1595431826440
 6 >>> id(copy_name)
 7 1595435745864                         #查看原字典和副本字典的内存地址,地址不同
 8 >>> id(name[‘sailor‘])
 9 1595437385096
10 >>> id(copy_name[‘sailor‘])
11 1595437383816                         #查看原字典和副本字典列表值对应的键下存储的内存地址,地址不同。说明当键对应的值是一个复杂的数据结构时deepcopy会继续复制到下一层。在浅copy中原字典和副本字典的这
12 >>> id(name[‘sailor‘][1])             #里是相同的。
13 1595435816248
14 >>> id(copy_name[‘sailor‘][1])
15 1595435816248                         #当查看原字典和副本字典中列表值下的元素的地址时发现,其内存地址又是相同的。

  由上面的内存地址的查看可以绘制出深copy时copy的范围对应的图

  可以对比浅copy时的结果的图中不一样的地方来理解。

  

高级语言中,变量是对内存及其地址的抽象。在python中变量的内存地址和变量值的内存地址是分开的--python中内存的变量存储情况。

每个变量中都存储了这个变量的地址,而不是值本身;对于复杂的数据结构来说,里面存储的也只是每个元素的内存地址而已,并不是值本身;

在python中执行一条赋值语句如a=‘abc‘python解释器做了两件事情:

1.在内存中创建了一个字符串‘abc’

2.在内存中创建了一个名为a的变量,并把它指向‘abc’

变量的每次初始化,都开辟了一个新的空间,并把新内容的内存地址赋值给变量

  

原文地址:https://www.cnblogs.com/freeasyman/p/8759664.html

时间: 2024-10-27 13:35:23

Python_赋值和深浅copy的相关文章

变量赋值,深浅copy

原文地址:https://blog.51cto.com/10983441/2382907

python深浅copy和赋值

Python直接赋值,浅copy和深copy的比较 基于引用和对象(python引用和对象分离) 总结: 直接赋值:a = b -->a,b两个引用指向相同的对象 浅copy:a为b的copy --> copy(新建)b的不可变子对象,共用可变子对象 深copy:a为b的深copy-->copy(新建)b的所有子对象,两者对象无关联 ps:对象的值相等,内存地址不一定相同(小数据池) 详细解析如下: 在python中,对象赋值实际上是对象的引用.当创建一个对象,然后把它赋给另一个变量的时

【python】-- 深浅copy、集合

深浅copy 1.数字.字符串的copy: 赋值(=).浅拷贝(copy)和深拷贝(deepcopy)其实都一样,因为它们永远指向同一个内存地址: >>> import copy >>> n1 = 123 >>> print(id(n1)) #打印n1的内存地址 502665488 ##赋值## >>> n2 = n1 >>> print(id(n2)) 502665488 ##浅拷贝## >>>

python深浅copy探究

引入 在python程序中,如果我们操作一个变量的值去做运算,而又想在下次调用时,仍使用原来的变量的值去做运算,那么我们我们就需要将这个变量去做备份,这就是本文所要探究的问题. 开始 变量-对象-引用: python中全部皆对象,Python中变量是指对象(甚至连type其本身都是对象,type对象)的引用,Python是动态类型,程序运行时候,会根据对象的类型来确认变量到底是什么类型. 我们有时候会见到这样一种情况: a = 1 b = a 这样做不就是把数据copy了一份吗,错,这样做只是在

python——赋值与深浅拷贝

初学编程的小伙伴都会对于深浅拷贝的用法有些疑问,今天我们就结合python变量存储的特性从内存的角度来谈一谈赋值和深浅拷贝~~~ 预备知识一--python的变量及其存储 在详细的了解python中赋值.copy和deepcopy之前,我们还是要花一点时间来了解一下python内存中变量的存储情况. 在高级语言中,变量是对内存及其地址的抽象.对于python而言,python的一切变量都是对象,变量的存储,采用了引用语义的方式,存储的只是一个变量的值所在的内存地址,而不是这个变量的只本身.  引

python之深浅copy

三.深浅copy调用方法:import copy #浅拷贝copy.copy() 深拷贝copy.deepcopy() #赋值1. 对于 数字 和 字符串 而言,赋值.浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址.举例:import copy#字符串,数字a1 = 'adddddd'a2 = '12322'a2 = a1print(id(a1))print(id(a2))a3 = copy.deepcopy(a1)print(id(a1))print(id(a3))输出结果:4192344

基础数据类型之集合和深浅copy,还有一些数据类型补充

集合 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 去重,把一个列表变成集合,就自动去重了. 关系测试,测试两组数据之前的交集.差集.并集等关系. #关于集合和波尔值之间的苟苟且且# set ={1,2,3,4,5,True,(1,2,3),} #集合具有自动去重和去空格功能# set.add(False)# set.add(True) #打印结果显示,没有Teue,应该是集合内部的数字和元组不

Python 集合 深浅copy

一,集合. 集合是无序的,不重复的数据集合,它里面的元素是可哈希的(不可变类型),但是集合本身是不可哈希(所以集合做不了字典的键)的.以下是集合最重要的两点: 去重,把一个列表变成集合,就自动去重了. 关系测试,测试两组数据之前的交集.差集.并集等关系. 1,集合的创建. set1 = set({1,2,'barry'}) set2 = {1,2,'barry'} print(set1,set2) # {1, 2, 'barry'} {1, 2, 'barry'} 2,集合的增. set1 =

python之旅 1-29 补充内容,深浅copy

#  l1 = [111,222,333,444,555] 将索引为奇数位的元素删除. # l1 = [111,222,333,444,555] # del l1[1::2] # print(l1) 通过删除列表中的元素 # for i in range(len(l1)-1,0,-1): # if i%2==0: # del l1[i-1] # print(l1) 通过循环列表,用range指代索引位置 for i in l1: if l1.index(i)%2==0: l2.append(i)