鱼C论坛

 找回密码
 立即注册
查看: 3069|回复: 5

[技术交流] python细节之2、变量名与标签 变量的赋值与修改

[复制链接]
发表于 2019-1-14 13:56:24 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 sunrise085 于 2020-3-16 13:02 编辑

在python编程中,我们经常会用到各种变量,每个变量都会有变量名,在程序中我们还可能会对变量进行各种修改操作。我们修改变量的时候到底修改的是什么呢?(昨天我再写这个帖子的时候,看到有个鱼油提出了很相近的问题,我感觉需要加快发布这个帖子,和大家进行一下交流)

话不多说,直接结合例程进行分析~~

  1. num1=123
  2. num2=num1
  3. num1+=333
  4. print('num1=',num1)
  5. print('num2=',num2)

  6. print()
  7. list1=['A','B','C']
  8. list2=list1
  9. list1.append('D')
  10. print('list1=',list1)
  11. print('list2=',list2)
复制代码

程序运行结果:

  1. num1= 456
  2. num2= 123

  3. list1= ['A', 'B', 'C', 'D']
  4. list2= ['A', 'B', 'C', 'D']
复制代码


这个例程中,共有两部分内容。第一部分是对整型变量int的赋值与修改操作,第二部分程序时对列表list的赋值与修改操作。但是运行结果却不一样。这是为啥呢?
这就需要理解python中变量空间的分配、变量名的指向等知识。
第一部分,下图左侧是执行完前两行代码的结果(地址是随便编的),右侧则是执行完第三行代码的结果。
1.jpg
第二部分,下图左侧是执行完第7、8行的结果,右侧是执行完第9行的结果。
1.jpg

在python中,整型int、字符串string、列表list、集合set、字典dict等等都可称之为对象,而这些不同类型的对象是有区别的,可以分为两类:不可变类型对象和可变类型对象。数字numbers、字符串string、元组tuples等是不可变类型对象,列表list、字典dict、集合set等是可变类型对象。

不可变类型对象就是一旦生成,就不可更改的对象,而可变类型对象是生成之后可以进行增删改等修改操作的对象。

对于不可变类型对象的修改操作实际上是创建了一个新的对象,然后将变量名(即标签)贴在新的对象上,这样我们就以为是修改了该变量了。对于可变类型对象的修改操作就是对原对象的修改。
变量名实际上应该称之为标签,我们会把各个标签贴在各个对象上,同一个对象可以有多个标签,但是一个标签不能贴在多个对象上。
在这个例程中,先看第一部分程序。首先生成一个int类型对象123,并将标签num1贴在该对象上,这个对象是不可变类型的对象;然后在该对象上又贴了一个标签num2;第三句程序是num1=num1+222,这里并不是对int类型的对象123进行修改,因为该类型的对象是不可修改的,实际上是生成一个新的对象,对象的值是123+222=456,然后将num1标签贴在该对象上。此时有两个int型的对象,分别贴有标签num1和num2,输出两个标签所指向的对象值,即为程序运行的结果。

再看第二部分程序,首先生成一个list类型的对象['A','B','C'],并将标签list1贴在该对象上,这个对象是可变类型的对象;然后在该对象上又贴了一个标签list2;第三句程序list1.append('D'),这里是直接对之前生成的list类型对象['A','B','C']进行的元素追加修改,是在原对象的位置进行的修改,并没有创建新对象。因此此时仍然只有一个对象,标签list1和list2都贴在了这个对象上,输出两个标签所指向的对象值自然就是同一个结果了。

那么若是想创建两个完全一样的可变类型对象应该如何创建呢,直接再敲一遍代码吗?
肯定不需要啊!
看以下程序:

  1. import copy
  2. mylist1=[[1,2,3],2,3]
  3. mylist2=mylist1                    #标签赋值
  4. mylist3=mylist1[:]                 #切片赋值,或者称之为浅拷贝,相当于mylist3=copy.copy(mylist1)
  5. mylist4=copy.deepcopy(mylist1) #深度拷贝赋值
  6. mylist1[1]=9
  7. mylist1[0][1]=8
  8. print('mylist1=',mylist1)
  9. print('mylist2=',mylist2)
  10. print('mylist3=',mylist3)
  11. print('mylist4=',mylist4)
复制代码

程序运行结果:

  1. mylist1= [[1, 8, 3], 9, 3]
  2. mylist2= [[1, 8, 3], 9, 3]
  3. mylist3= [[1, 8, 3], 2, 3]
  4. mylist4= [[1, 2, 3], 2, 3]
复制代码


这段程序中用了三种不同的方法将标签mylist1的对象分别赋值给三个新的标签,然后对mylist1的变量进行了修改。最后输出这四个标签所指向的对象。然后发现mylist1和mylist2完全一样,这个在上文中已经讲了。
接下来我们看看mylist3,这个是切片赋值,切片赋值是创建了一个新对象,因此将原对象mylist1的元素1改为9,不影响mylist3的值。接着我们有对mylist1的子对象进行了修改,发现mylist3的子对象也跟着修改了。为啥呢?不是创建新对象了么?咋还跟着原对象变了呢?这里就设计到另外一个知识点,就是python的浅拷贝和深拷贝。切片赋值是浅拷贝,相当于mylist3=copy.copy(mylist1),只拷贝的父对象,而不会拷贝里面的子对象,子对象仍然是原来对象的子对象,因此就出现了以上现象。要想完全拷贝赋值,则需要使用深拷贝,即copy.deepcopy(),deepcopy()方法可以进行完全拷贝,既对父对象进行了拷贝,也对子对象进行了拷贝。是一个全新的对象了。因此mylist4完全不受mylist1的影响了。


注意:深拷贝需要引入copy模块!!

以上就是个人对变量的修改与赋值的相关解析,看了一些相关资料之后进行的整理。可能会有不足之处,希望大家多多交流,相互交流,一起讨论,共同进步!!

评分

参与人数 1荣誉 +5 鱼币 +5 贡献 +3 收起 理由
uupppo + 5 + 5 + 3

查看全部评分

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-1-16 15:48:57 | 显示全部楼层
解决了我的疑惑,谢谢楼主
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-1-23 20:55:19 | 显示全部楼层
楼主,您好,想再问您一下,print('num1=',num1)输出456,那么num1 = 123的变量去哪儿了呢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-13 11:56:22 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-4-10 10:04:15 From FishC Mobile | 显示全部楼层
书单狗好评
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-5-28 15:35:11 | 显示全部楼层
太棒了,不仅理解了存储机制、可变对象、不可变对象,还有浅拷贝、深拷贝的区别,感谢!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-7 06:00

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表