作為一個python初學者,今天被一個python列表和詞典引用的問題折磨了很久,但其實了解了緣由也很簡單,記錄在此備忘。
首先背書python中的引用對象問題:
1. python不允許程序員選擇采用傳值還是傳引用。Python參數傳遞采用的肯定是“傳對象引用”的方式。實際上,這種方式相當於傳值和傳引用的一種綜合。如果函數收到的是一個可變對象(比如字典或者列表)的引用,就能修改對象的原始值——相當於通過“傳引用”來傳遞對象。如果函數收到的是一個不可變對象(比如數字、字符或者元組)的引用,就不能直接修改原始對象——相當於通過“傳值'來傳遞對象。
2. 當人們復制列表或字典時,就復制了對象列表的引用同,如果改變引用的值,則修改了原始的參數。
3. 為了簡化內存管理,Python通過引用計數機制實現自動垃圾回收功能,Python中的每個對象都有一個引用計數,用來計數該對象在不同場所分別被引用了多少次。每當引用一次Python對象,相應的引用計數就增1,每當消毀一次Python對象,則相應的引用就減1,只有當引用計數為零時,才真正從內存中刪除Python對象。
列表引用
首先看2個示例:
1 def add_list(p): 2 p = p + [1] 3 p1 = [1,2,3] 4 add_list(p1) 5 print p1 6 >>> [1, 2, 3] 7 8 def add_list(p): 9 p += [1] 10 p2 = [1,2,3] 11 proc2(p2) 12 print p2 13 >>>[1, 2, 3, 1]
這主要是由於“=”操作符會新建一個新的變量保存賦值結果,然后再把引用名指向“=”左邊,即修改了原來的p引用,使p成為指向新賦值變量的引用。而+=不會,直接修改了原來p引用的內容,事實上+=和=在python內部使用了不同的實現函數。
詞典引用
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 []
這里再備忘一些關於列表和詞典的操作:
列表 list[]
賦值 list1[3:4]=[a,b]
len(list)長度
del list 刪除對象
列表對象支持的方法:
append(x) 尾部追加 單個對象x,使用多個對象會引起異常。
count(x) 返回對象x在list中出現的次數
extend(L) 將列表L中的項添加到表中
index(x) 返回匹配對象x第一個表項的索引,無匹配時產生異常
insert(i,x) 在索引‘i’的元素錢插入對象x
pop(x) 刪除列表中索引x的表項,並返回同該表項的值,無參數刪除最后
remove(x) 刪除表匹配對象x的第一個元素,無匹配時異常
reverse() 顛倒列表元素的順序
sort() 對列表排序
此外可簡單使用+實現列表連接:[3,4] + [[1,2],5,6] --> [3,4,[1,2],5,6]
刪除列表中的重復項:M = list(set(L)),python的set和其他語言類似, 是一個無序不重復元素集
詞典 dictionary{name:value,...}
+++字典的方法
has_keys(x) 若字典中有x返回true
keys() 返回鍵的列表
values() 返回值的列表
dict.items() 返回tuples的列表。每個tuple有字典的dict的鍵和相應的值組成
clear() 刪除詞典的所有條目
copy() 返回字典的高層結構的拷貝,但不復制嵌入結構,而復制那些結構的引用。
update(x) 用字典x中的鍵/值對更新字典的內容。
get(x[,y]) 返回鍵x。若未找到返回None
內置對象類型轉換
str(x) 將對象x翻譯為字符串
list(x) 將對象序列x作為列表返回。例如‘hello’返回['h','e','l','l','o'],將tuple轉換為列表
tuple(x) 將對象序列x作為tuple返回
int(x) 將字符串和數字轉換為整數,對浮點進行舍位而非舍入
long(x) 將字符串和數字轉換為長整形
float(x) 將str和num轉換為浮點對象
complex(x,y) 將x做實部,y做虛部創建復數
hex(x) 將整數或長整數轉換為十六進制字符串
oct(x) 將整數或長整數轉換為八進制字符串
ord(x) 返回字符x的ASCII值
chr(x) 返回ASCII碼x代表的字符
min(x[,...]) 返回序列中最小的元素
max(x[,...]) 返回序列中最大的元素
參考
http://opengit.org/open/?f=python_note
http://blog.csdn.net/wayne92/article/details/1133465
http://hliang.sinaapp.com/?p=233