前言
为什么我要用字典类型做测试的载体
因为在实际开发的过程中,字典是使用频率非常高的一种数据结构,而且不同场景要构造不一样的字典,偶尔踩坑,还是要总结一下经验的,长长记性。
测试1:列表类型
# test1
tt_list = [0 for i in range(3)]
dd = dict()
dd.setdefault('a', tt_list)
dd['a'][0] += 1
dd.setdefault('b', tt_list)
dd['b'][1] += 1
dd.setdefault('c', tt_list)
dd['c'][2] += 1
dd
期望输出:
{'a': [1, 0, 0], 'b': [0, 1, 0], 'c': [0, 0, 1]}
实际输出:
{'a': [1, 1, 1], 'b': [1, 1, 1], 'c': [1, 1, 1]}
tt_list 为列表推导式赋值的列表,这里测试了在同一个字典对象里的初始化赋值问题,赋值目标为列表类型。
查看字典里元素值对应的内存地址:
id(dd['a']), id(dd['b']), id(dd['c'])
输出:(140408677234432, 140408677234432, 140408677234432)
明显地,上面用变量去初始化赋值并不是理想的。问题出在用tt_list初始化赋值的时候,实际字典变量指向的是tt_list变量的引用,连续赋值了3次,其实都指向了同一个内存地址,所以任何对字典变量的操作都会影响全局。
正确示例
# tt_list = [0 for i in range(3)]
dd = dict()
dd.setdefault('a', [0 for i in range(3)])
dd['a'][0] += 1
dd.setdefault('b', [0 for i in range(3)])
dd['b'][1] += 1
dd.setdefault('c', [0 for i in range(3)])
dd['c'][2] += 1
dd
输出:
{'aa': [1, 0, 0], 'bb': [0, 1, 0], 'cc': [0, 0, 1]}
查看字典元素值的内存地址:
id(dd['a']), id(dd['b']), id(dd['c'])
输出:(140408929043456, 140408929188160, 140408929171072)
测试2:字符串
tt_str = ''
dd = dict()
dd.setdefault('a', tt_str)
dd['a'] = 'a'
dd.setdefault('b', tt_str)
dd['b'] = 'b'
dd.setdefault('c', tt_str)
dd['c'] = 'c'
dd
输出:
{'a': 'a', 'b': 'b', 'c': 'c'}
第二个:
# tt_str = ''
dd = dict()
dd.setdefault('a', '')
dd['a'] = 'aa'
dd.setdefault('b', '')
dd['b'] = 'bb'
dd.setdefault('c', '')
dd['c'] = 'cc'
dd
输出: {'a': 'aa', 'b': 'bb', 'c': 'cc'}
查看下内存地址:
id(dd['a']), id(dd['b']), id(dd['c'])
输出:(140408404501936, 140408395757296, 140408394948144)
总结
实验做到这一步,结论有了,虽然问题看起来是变量赋值和引用的区别,但其实涉及到的知识点是python中的可变对象与不可变对象。
不可变对象 :int,str,float,tuple – 可理解为C中,值传递
可变对象 :list,dict – 可理解为C中,指针传递
还不明白的话,可以继续拿其它数据类型做测试。