关于容器对象、可变对象、append方法、for循环遍历的结合:
例题:将字典person的id循环遍历3次后,加入列表team中,再在列表team中加入name值。[1]
第一种代码:
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'] = 'Crossion'
print(team)
输出结果:
结果,并不是想象中,包含id=0,name=Jack,id=1,name=Pony等的对应值的列表。
为了便于查看问题,在过程代码中,重新补充了print输出,
演示:
person = {'name': '', 'id': 0}
team = []
for i in range(3):
x = person
x['id'] = i
print('person =',person,'nteam =',team)
team.append(x)
print('person =',person,'nteam =',team)
print('-'*10)
team[0]['name'] = 'Jack'
print('person =',person,'nteam =',team)
print('-'*10)
team[1]['name'] = 'Pony'
print('person =',person,'nteam =',team)
print('-'*10)
team[2]['name'] = 'Crossin'
print('person =',person,'nteam =',team)
print('-'*10)
输出显示
原因:
容器对象person字典,变量 x 引用可变的容器对象person字典,且for遍历id,每一次都在person字典原地进行修改。
team列表append方法,每一次增加的变量x,都引用同一个可变容器对象person。
每当for循环一次,字典person的id被修改,作为容器对象的字典person 原地修改后(内存地址不变),
由于列表team内增加的每一个元素,都同时引用字典person,于是元素都同时改变。
原理图
http://www.pythontutor.com/visualize.html#code=person%20%3D%20%7B'name'%3A%20'',%20'id'%3A%200%7D%0Ateam%20%3D%20%5B%5D%0A%0Afor%20i%20in%20range%283%29%3A%0A%20%20%20%20x%20%3D%20person%0A%20%20%20%20x%5B'id'%5D%20%3D%20i%0A%20%20%20%20print%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0A%20%20%20%20team.append%28x%29%0A%20%20%20%20print%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0A%20%20%20%20print%28'-'*10%29%0A%0A%0Ateam%5B0%5D%5B'name'%5D%20%3D%20'Jack'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0Ateam%5B1%5D%5B'name'%5D%20%3D%20'Pony'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0Ateam%5B2%5D%5B'name'%5D%20%3D%20'Crossin'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29&cumulative=true&curInstr=33&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false
第二种代码:
删除变量 x ,for循环直接遍历字典person的id,代码如下:
person = {'name': '', 'id': 0}
team = []
for i in range(3):
person['id'] = i
team.append(person)
team[0]['name'] = 'Jack'
team[1]['name'] = 'Pony'
team[2]['name'] = 'Crossion'
print(team)
输出依然是:
演示:
person = {'name': '', 'id': 0}
team = []
for i in range(3):
person['id'] = i
print('person =',person,'nteam =',team)
team.append(person)
print('person =',person,'nteam =',team)
print('-'*10)
team[0]['name'] = 'Jack'
print('person =',person,'nteam =',team)
print('-'*10)
team[1]['name'] = 'Pony'
print('person =',person,'nteam =',team)
print('-'*10)
team[2]['name'] = 'Crossin'
print('person =',person,'nteam =',team)
print('-'*10)
输出:
问题存在:
即使没有了变量x,在for循环每次遍历id后,每当id修改一次,person就在原地修改一次,列表team中append追加的元素,依然都指向同一个容器对象字典person内。
http://www.pythontutor.com/visualize.html#code=person%20%3D%20%7B'name'%3A%20'',%20'id'%3A%200%7D%0Ateam%20%3D%20%5B%5D%0A%0Afor%20i%20in%20range%283%29%3A%0A%20%20%20%20person%5B'id'%5D%20%3D%20i%0A%20%20%20%20print%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0A%20%20%20%20team.append%28person%29%0A%20%20%20%20print%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0A%20%20%20%20print%28'-'*10%29%0A%0A%0Ateam%5B0%5D%5B'name'%5D%20%3D%20'Jack'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0Ateam%5B1%5D%5B'name'%5D%20%3D%20'Pony'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0Ateam%5B2%5D%5B'name'%5D%20%3D%20'Crossin'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A&cumulative=false&curInstr=19&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false
如何让for每一次遍历id时,在不同的person字典呢?
for 循环遍历id时,要顺便生成不同的person内存地址。
第三种代码:
team = []
for i in range(3):
person = {'name': '', 'id': 0}
person['id'] = i
team.append(person)
team[0]['name'] = 'Jack'
team[1]['name'] = 'Pony'
team[2]['name'] = 'Crossin'
print(team)
输出:
演示代码:
team = []
for i in range(3):
person = {'name': '', 'id': 0}
person['id'] = i
print('person =',person,'nteam =',team)
team.append(person)
print('person =',person,'nteam =',team)
print('-'*10)
team[0]['name'] = 'Jack'
print('person =',person,'nteam =',team)
print('-'*10)
team[1]['name'] = 'Pony'
print('person =',person,'nteam =',team)
print('-'*10)
team[2]['name'] = 'Crossin'
print('person =',person,'nteam =',team)
print('-'*10)
输出:
person在for循环中,每一次for开始遍历时,都会初始化字典person,
for i in range(3):
person = {'name': '', 'id': 0}
创建一个新的对象,id遍历后,所在的不同的字典person,不再是原地修改person,所以列表team使用append方法时,每次引用不同person,元素之间相互独立,不会被原地修改。
原理图:
http://www.pythontutor.com/visualize.html#code=team%20%3D%20%5B%5D%0Afor%20i%20in%20range%283%29%3A%0A%20%20%20%20person%20%3D%20%7B'name'%3A%20'',%20'id'%3A%200%7D%0A%20%20%20%20person%5B'id'%5D%20%3D%20i%0A%20%20%20%20print%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0A%20%20%20%20team.append%28person%29%0A%20%20%20%20print%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0A%20%20%20%20print%28'-'*10%29%0A%0A%0Ateam%5B0%5D%5B'name'%5D%20%3D%20'Jack'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0Ateam%5B1%5D%5B'name'%5D%20%3D%20'Pony'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0Ateam%5B2%5D%5B'name'%5D%20%3D%20'Crossin'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29&cumulative=true&curInstr=32&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false
第四种代码:
使用浅拷贝方法:
from copy import copy
person = {'name': '', 'id': 0}
team = []
for i in range(3):
x = person.copy()
x['id'] = i
team.append(x)
team[0]['name'] = 'Jack'
team[1]['name'] = 'Pony'
team[2]['name'] = 'Crossin'
print(team)
演示:
from copy import copy
person = {'name': '', 'id': 0}
team = []
for i in range(3):
x = person.copy()
x['id'] = i
print('person =',person,'nteam =',team)
team.append(x)
print('person =',person,'nteam =',team)
print('-'*10)
team[0]['name'] = 'Jack'
print('person =',person,'nteam =',team)
print('-'*10)
team[1]['name'] = 'Pony'
print('person =',person,'nteam =',team)
print('-'*10)
team[2]['name'] = 'Crossin'
print('person =',person,'nteam =',team)
print('-'*10)
输出:
浅拷贝x = person.copy(),原始容器对象person一直未改变,每次变量x = {'name': '', 'id': 0},x 引用字典对象的值:{'name': '', 'id': 0},与第三种代码一致。
for i in range(3):
x = person.copy()
x['id'] = i
等同于
for i in range(3):
x = {'name': '', 'id': 0}
x['id'] = i
原理图:
http://www.pythontutor.com/visualize.html#code=from%20copy%20import%20copy%0Aperson%20%3D%20%7B'name'%3A%20'',%20'id'%3A%200%7D%0Ateam%20%3D%20%5B%5D%0A%0Afor%20i%20in%20range%283%29%3A%0A%20%20%20%20x%20%3D%20person.copy%28%29%0A%20%20%20%20x%5B'id'%5D%20%3D%20i%0A%20%20%20%20print%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0A%20%20%20%20team.append%28x%29%0A%20%20%20%20print%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0A%20%20%20%20print%28'-'*10%29%0A%0A%0Ateam%5B0%5D%5B'name'%5D%20%3D%20'Jack'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0Ateam%5B1%5D%5B'name'%5D%20%3D%20'Pony'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0Ateam%5B2%5D%5B'name'%5D%20%3D%20'Crossin'%0Aprint%28'person%20%3D',person,'%5Cnteam%20%3D',team%29%0Aprint%28'-'*10%29%0A%0A&cumulative=true&curInstr=34&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false
参考
- ^例题 https://zhuanlan.zhihu.com/p/90484197