在多年开发和教学 Python 的过程中,有一个 bug,出现的频率非常高,经常有人会踩坑:
person = {'name': '', 'id': 0} team = [] for i in range(3): x = person x['id'] = i team.append(x) team[0]['name'] = 'Jack' team[1]['name'] = 'Pony' team[2]['name'] = 'Crossin' print(team[1])
如果觉得这个输出结果是 {‘name’: ‘Pony’, ‘id’: 1} ,那么就掉进这个坑里了。
如果真的运行一下就会发现,team里面三个都是 {‘name’: ‘Crossin’, ‘id’: 2} , 而且,如果之后再更改任何一个的 name 或 id 属性,另外两个也会跟着变。
那么为什么呢?
在 Python 中,要把变量想象成一个标签,而不是一个容器!
什么意思?在某些语言中,变量确实像一个“容器”,你定义了某种类型的变量,就给你分配好这个容器,之后你给变量赋值,就像是往容器里装入不同的内容,但容器还是那个容器,不会变。你创建3个容器,赋给一样的值,他们也还是3个独立的容器。
但在 Python 中,这个理解是错误的!
Python 中的变量像是一个“标签”,你给一个变量赋值,就是把这个标签贴在一个对象上;重新赋值,就是撕下标签帖到另一个对象上。
给3个变量赋给一样的值,就相当于把3个标签贴在同一个对象上。
用例子来说明:
a = 1 b = 2 c = 1 # 再次赋值 a = b
示意图:
通过输出 id(相当于内存地址)也可以说明这点:
在赋值之后,其实并不是 a 的值发生了变化,而是 a 的地址发生了变化。
理解了这点之后,你就会明白2件事:
- 当你给一个变量重新赋值之后,它就不再是之前的那个变量,所有的操作不会再影响到之前的变量上
- 当你给多个变量赋值相同的变量,它们其实都是同一个,只要改动其中之一,其他的也会跟着变化。(注意,是改动而非重新赋值,比如修改对象属性)
这个原理,在有关函数的参数传递、拷贝对象时都会涉及到。
因此,回到最初的问题,三个重新赋值其实本质是三个共用同一地址。
本文转载自微信公众号 Crossin的编程教室 ,为以后避免踩坑而记录。
有时候一天长的像一生,
有时候一生短的像一天。
世界上没有两个相同的一天,
但每一天都适合开始,
适合离别,适合爱。
——周一周二
评论
786424 112878Wohh just what I was seeking for, appreciate it for putting up. 558409