Python 修改一个对象的值对相关对象的影响
python提供了方便的初始化复杂对象的操作,比如使用循环或者*快速构建一个list,而不用一个一个元素手敲进去,但是这样的初始化方式可能带来意想不到的结果。
1.使用*快速构建一个list
list1 = [1, 1, 1]
list2 = [2] * 3
print(list2)
# [2, 2, 2]
print(id(list1[0]), id(list1[1]), id(list1[2]))
# 94726649605440 94726649605440 94726649605440
print(id(list2[0]), id(list2[1]), id(list2[2]))
# 94726649605472 94726649605472 94726649605472
list1[0] = 100
print(list1)
# [100, 1, 1]
list2[0] = 200
print(list2)
# [200, 2, 2]
print(id(list1[0]), id(list1[1]), id(list1[2]))
# 94726649608608 94726649605440 94726649605440
print(id(list2[0]), id(list2[1]), id(list2[2]))
# 94726649611808 94726649605472 94726649605472
id()函数返回对象的内存地址
这里由于python的小整数机制,所有1在内存中只有一个示例,所有2在内存中也只有一个示例,虽然list1和list2的3个元素指向同一块内存,但是修改其中1个的值不会影响到其他2个的值
2.构建一个复杂的list
自定义一个类,将类的对象作为list的元素
class A():
def __init__(self):
self.v = 10
a = A()
a2 = A()
list3 = [a, a, a]
list4 = [a2] * 3
print(id(list3[0]), id(list3[1]), id(list3[2]))
# 140041873540864 140041873540864 140041873540864
print(id(list4[0]), id(list4[1]), id(list4[2]))
# 140041873540336 140041873540336 140041873540336
list3[0].v = 100
list4[0].v = 200
print(list3[0].v, list3[1].v, list3[2].v)
# 100 100 100
print(list4[0].v, list4[1].v, list4[2].v)
# 200 200 200
print(id(list3[0]), id(list3[1]), id(list3[2]))
# 140041873540864 140041873540864 140041873540864
print(id(list4[0]), id(list4[1]), id(list4[2]))
# 140041873540336 140041873540336 140041873540336
这个时候修改其中1个list的元素就会影响到其它2个元素
3.list中嵌套list(list的元素也是list)
这种情况下内层的list看成是一个复杂对象,如果复杂对象的内存地址相同,那么修改其中一个复杂对象,相关的复杂对象也会变化
list5 = [[5] * 3] * 3
print(list5)
# [
# [5, 5, 5],
# [5, 5, 5],
# [5, 5, 5]
# ]
print(id(list5[0][0]), id(list5[0][1]), id(list5[0][2]))
# 94759677586880 94759677586880 94759677586880
print(id(list5[0]), id(list5[1]), id(list5[2]))
# 139670761854336 139670761854336 139670761854336
list5[1][1] = 50
print(list5)
# [
# [5, 50, 5],
# [5, 50, 5],
# [5, 50, 5]
# ]
我们发现当使用*来将一个list按行进行扩展时,从一维扩展到二维
此时我们发现每一行的id值都是相等的,这说明3个行list指向同一块内存,修改其中一个list就会影响到另外2个list。
这里猜测大概是*的关系,将一个list [5,5,5]使用*扩展3份,此时这里的list相当于list4 = [a2] * 3中的a2,也就是其他2个list是使用同一个对象复制的,导致它们几个最后指向同一块内存,修改1个,另外2个也跟着变化。
换一种方式,每次创建list [6,6,6]的时候,都重新初始化
list6 = [[6] * 3 for _ in range(3)]
print(list6)
# [
# [6, 6, 6],
# [6, 6, 6],
# [6, 6, 6]
# ]
print(id(list6[0][0]), id(list6[0][1]), id(list6[0][2]))
# 94759677586912 94759677586912 94759677586912
print(id(list6[0]), id(list6[1]), id(list6[2]))
# 139670761854144 139670761854272 139670761899968
list6[1][1] = 60
print(list6)
# [
# [6, 6, 6],
# [6, 60, 6],
# [6, 6, 6]
# ]
使用这种方式发现3个行list的id是不同的,也就是在内存中分属于不同的区域
python二维列表List修改其中一个数值,所有项数值都改变的问题
当然,手动输入一个3*3的数组肯定也没问题
list7 = [
[7, 7, 7],
[7, 7, 7],
[7, 7, 7]
]
print(id(list7[0][0]), id(list7[0][1]), id(list7[0][2]))
# 93963458526720 93963458526720 939634585267202
print(id(list7[0]), id(list7[1]), id(list7[2]))
# 140037817369088 140037816002240 140037816002048
list7[1][1] = 70
print(list7)
# [
# [7, 7, 7],
# [7, 70, 7],
# [7, 7, 7]
# ]
4.个人遇到的问题
构建一个二维的数组(用list表示),然后数组的每个元素也是空的list[]
# 方法1:手动输入
table1 = [
[ [], [], [] ],
[ [], [], [] ],
[ [], [], [] ]
]
# 方法2:循环构建
row = [[] for _ in range(3)]
print(row)
# [ [], [], [] ]
table2 = [row for _ in range(3)]
print(table2)
# [
# [ [], [], [] ],
# [ [], [], [] ],
# [ [], [], [] ]
# ]
用的方法2构建的table2
检查table1和table2中元素的内存地址
print(id(table1[0][0]), id(table1[0][1]), id(table1[0][2]))
# 140667263779328 140667262412288 140667262458048
print(id(table1[0]), id(table1[1]), id(table1[2]))
# 140667262457984 140667262412224 140667262459008
print(id(table2[0][0]), id(table2[0][1]), id(table2[0][2]))
# 140667262459200 140667262459264 140667262459328
print(id(table2[0]), id(table2[1]), id(table2[2]))
# 140667262459136 140667262459136 140667262459136
print(id(row))
# 140667262459136
可以看到table2的3个行其实是同一块内存,也是是row对象所在的地址,原因在于使用row对象重复构建行
现在尝试对table1和table2的元素进行操作,对下标为[1][1]元素进行数据插入。
data = [1, 2, 3]
table1[1][1].extend(data)
table2[1][1].extend(data)
print(table1)
# [
# [ [], [], [] ],
# [ [], [1, 2, 3], [] ],
# [ [], [], [] ]
# ]
print(table2)
# [
# [ [], [1, 2, 3], [] ],
# [ [], [1, 2, 3], [] ],
# [ [], [1, 2, 3], [] ]
# ]
print(row)
# [ [], [1, 2, 3], [] ]
table1的结果正常
table2整个第2列都被修改了,而且row的值也跟着修改了
所以在创建table的时候不要使用同一个对象复制多份,也就是不要像table2那样多次使用row
使用table3的方式就可以得到正常的结果
table3 = [[[] for i in range(3)] for j in range(3)]
print(table3)
# [
# [ [], [], [] ],
# [ [], [], [] ],
# [ [], [], [] ]
# ]
print(id(table3[0][0]), id(table3[0][1]), id(table3[0][2]))
# 140155553321024 140155553321408 140155553321536
print(id(table3[0]), id(table3[1]), id(table3[2]))
# 140155553321472 140155553321600 140155553321856
table3[1][1].extend(data)
print(table3)
# [
# [ [], [], [] ],
# [ [], [1, 2, 3], [] ],
# [ [], [], [] ]
# ]
5. 补充实验
data = [1, 2, 3]
table4 = [[[] for i in range(3)]] * 3
print(table4)
# [
# [ [], [], [] ],
# [ [], [], [] ],
# [ [], [], [] ]
# ]
print(id(table4[0][0]), id(table4[0][1]), id(table4[0][2]))
# 140360386152064 140360386151552 140360386151616
print(id(table4[0]), id(table4[1]), id(table4[2]))
# 140360386151808 140360386151808 140360386151808
table4[1][1].extend(data)
print(table4)
# [
# [ [], [1, 2, 3], [] ],
# [ [], [1, 2, 3], [] ],
# [ [], [1, 2, 3], [] ]
# ]
这说明*的做法就是对同一个对象多次复制,类似于table2的做法
再次印证*
data=[1,2,3]
table5=[ [] for _ in range(3)]
print(table5)
# [ [], [], [] ]
print(id(table5[0]),id(table5[1]),id(table5[2]))
# 140717454157824 140717454157312 140717454157376
table6=[[]]*3
print(table6)
# [ [], [], [] ]
print(id(table6[0]),id(table6[1]),id(table6[2]))
# 140717454157504 140717454157504 140717454157504
table5[1].extend(data)
print(table5)
# [ [], [1, 2, 3], [] ]
table6[1].extend(data)
print(table6)
# [ [1, 2, 3], [1, 2, 3], [1, 2, 3] ]