作为一个python初学者,今天被一个python列表和词典引用的问题折磨了很久,但其实了解了缘由也很简单,记录在此备忘。
首先背书python中的引用对象问题:
1. python不允许程序员选择采用传值还是传引用。Python参数传递采用的肯定是“传对象引用”的方式。实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用(有点指针的意思),就能修改对象的原始值——相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象——相当于通过“传值'来传递对象。
2. 当人们复制列表或字典时,就复制了对象列表的引用同,如果改变引用的值,则修改了原始的参数,如;a=b;如果不想改变其原始的值,在复制时使用copy函数,如:a=b.copy(),也可以使用切片技术,如:a=b[:]
3. 为了简化内存管理,Python通过引用计数机制实现自动垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象。
列表引用
首先看2个示例:
这主要是由于“=”操作符会新建一个新的变量保存赋值结果,然后再把引用名指向“=”左边,即修改了原来的p引用,使p成为指向新赋值变量的引用。而+=不会,直接修改了原来p引用的内容,事实上+=和=在python内部使用了不同的实现函数。其实不止+=操作,其增删改操作均能改变其值。如:a.append("test")
词典引用
- 1 a = []
- 2 b = {'num':0, 'sqrt':0}
- 3 resurse = [1,2,3]
- 4 for i in resurse:
- 5 b['num'] = i
- 6 b['sqrt'] = i * i
- 7 a.append(b)
- 8 print a
- 9 >>> [{'num': 3, 'sqrt': 9}, {'num': 3, 'sqrt': 9}, {'num': 3, 'sqrt': 9}]
但我们实际想要的结果是这样的:
>>> [{'num': 1, 'sqrt': 1}, {'num': 2, 'sqrt': 4}, {'num': 3, 'sqrt': 9}]
这是由于a中的元素就是b的引用。可以修改为:
1 a = [] 2 resurse = [1,2,3] 3 for i in resurse: 4 a.append({"num": i, "sqrt": i * i})
实例
接下来可以看看折磨我半天的一个实例:
定义一个家族谱词典:value为key的parent. 要写一个函数,输入人名,给出这个人的所有祖先名字。
开始的做法:
1 ada_family = { 'Judith Blunt-Lytton': ['Anne Isabella Blunt', 'Wilfrid Scawen Blunt'], 2 'Ada King-Milbanke': ['Ralph King-Milbanke', 'Fanny Heriot'], 3 'Ralph King-Milbanke': ['Augusta Ada King', 'William King-Noel'], 4 'Anne Isabella Blunt': ['Augusta Ada King', 'William King-Noel'], 5 'Byron King-Noel': ['Augusta Ada King', 'William King-Noel'], 6 'Augusta Ada King': ['Anne Isabella Milbanke', 'George Gordon Byron'], 7 'George Gordon Byron': ['Catherine Gordon', 'Captain John Byron'], 8 'John Byron': ['Vice-Admiral John Byron', 'Sophia Trevannion'] } 9 10 11 def ancestors(genealogy, person): 12 if person in genealogy: 13 parents = genealogy[person] 14 result = parents 15 for parent in parents: 16 result += ancestors(genealogy, parent) 17 return result 18 return [] 19 print ancestors2(ada_family, 'Judith Blunt-Lytton') 20 print ada_family 21 22 #>>> ['Anne Isabella Blunt', 'Wilfrid Scawen Blunt', 'Augusta Ada King', 23 # 'William King-Noel', 'Anne Isabella Milbanke', 'George Gordon Byron', 24 # 'Catherine Gordon', 'Captain John Byron', 'Catherine Gordon', 25 # 'Captain John Byron', 'Anne Isabella Milbanke', 'George Gordon Byron', 26 # 'Catherine Gordon', 'Captain John Byron', 'Catherine Gordon', 27 # 'Captain John Byron', 'Catherine Gordon', 'Captain John Byron', 28 # 'Catherine Gordon', 'Captain John Byron'] 29 # 30 #>>> {'Ralph King-Milbanke': ['Augusta Ada King', 'William King-Noel'], 31 # 'Ada King-Milbanke': ['Ralph King-Milbanke', 'Fanny Heriot'], 32 # 'Anne Isabella Blunt': ['Augusta Ada King', 'William King-Noel', 'Anne Isabella Milbanke', 'George Gordon Byron', 'Catherine Gordon', 'Captain John Byron', 'Catherine Gordon', 'Captain John Byron'], 33 # 'Augusta Ada King': ['Anne Isabella Milbanke', 'George Gordon Byron', 'Catherine Gordon', 'Captain John Byron', 'Catherine Gordon', 'Captain John Byron'], 34 # 'Judith Blunt-Lytton': ['Anne Isabella Blunt', 'Wilfrid Scawen Blunt', 'Augusta Ada King', 'William King-Noel', 'Anne Isabella Milbanke', 'George Gordon Byron', 'Catherine Gordon', 'Captain John Byron', 'Catherine Gordon', 'Captain John Byron', 'Anne Isabella Milbanke', 'George Gordon Byron', 'Catherine Gordon', 'Captain John Byron', 'Catherine Gordon', 'Captain John Byron', 'Catherine Gordon', 'Captain John Byron', 'Catherine Gordon', 'Captain John Byron'], 35 # 'Byron King-Noel': ['Augusta Ada King', 'William King-Noel'], 36 # 'George Gordon Byron': ['Catherine Gordon', 'Captain John Byron'], 37 # 'John Byron': ['Vice-Admiral John Byron', 'Sophia Trevannion']}
这并不是我想要的结果,开始检查了好久都不明所以,直到我突然想起来打印词典ada_family,才发现词典已经不是原来的值了,这时,我才反应过来是引用的原因。由于我们使用的result实际就是词典中的value列表的引用,改动了result,就也改动了ada_family词典,从而导致结果不正确(有很多重复项)。
修改为如下写法即可。
1 def ancestors(genealogy, person): 2 if person in genealogy: 3 parents = genealogy[person] 4 result = parents 5 for parent in parents: 6 result = result + ancestors2(genealogy, parent) 7 return result 8 return [] 9 10 #>>> ['Anne Isabella Blunt', 'Wilfrid Scawen Blunt', 'Augusta Ada King', 11 # 'William King-Noel', 'Anne Isabella Milbanke', 'George Gordon Byron', 12 # 'Catherine Gordon', 'Captain John Byron'] 13 # 14 #>>> {'Ralph King-Milbanke': ['Augusta Ada King', 'William King-Noel'], 15 # 'Ada King-Milbanke': ['Ralph King-Milbanke', 'Fanny Heriot'], 16 # 'Anne Isabella Blunt': ['Augusta Ada King', 'William King-Noel'], 17 # 'Augusta Ada King': ['Anne Isabella Milbanke', 'George Gordon Byron'], 18 # 'Judith Blunt-Lytton': ['Anne Isabella Blunt', 'Wilfrid Scawen Blunt'], 19 # 'Byron King-Noel': ['Augusta Ada King', 'William King-Noel'], 20 # 'George Gordon Byron': ['Catherine Gordon', 'Captain John Byron'], 21 # 'John Byron': ['Vice-Admiral John Byron', 'Sophia Trevannion']}
此时就不会修改原词典内容,得到的也是正确结果。
当然,我们也可以简单的使用以下方法实现,就避免了引用带来的麻烦:
1 def ancestors(genealogy, person): 2 if person in genealogy: 3 parents = genealogy[person] 4 return parents + ancestors(genealogy,parents[0]) + ancestors(genealogy,parents[1]) 5 return []