以下是自己的學習記錄,算是一個總結。
接下來會依次對下面問題做一個解答:
1. Python的dict和set為什么是無序的?
2. 為什么不是所有的python對象都可以用作dict的鍵和set中的元素
要弄懂上面的問題,我們首先要了解Python內部是如何實現dict和set類型的。我們先來看看dict的內部結構,dict其實本質上是一個散列表(散列表即總有空白元素的數組,Python會保證至少有三分之一的數組元素是空的),dict的每個鍵都占用一個表元,而一個表元中又分為兩個部分,分別是對鍵的引用和對值的引用。當我們存放一個對象的時候,首先會要計算這個元素的散列值,python中使用hash()方法來實現的,這也就回答了第二個問題,因為不是所有的python對象都可以使用hash來獲取散列值,獲取不到散列值也就不可能存放到dict中,所以只有可hash的對象才能夠作為dict的鍵。值得注意的是內置的hash方法可以用於所有的內置類型對象的,所有用戶自定義的對象默認都是可以作為鍵的,因為自定義對象的散列值是通過id()來獲取的。例如:
class T(object):
pass
t = T()
print(id(t))
d = {t: 1}
print(d)
### 2133693018240
### {<__main__.T object at 0x000001F0CA03B080>: 1}
現在假設我們已經獲取到了元素的散列值,接下來就該計算應當存放位置了,將散列值對數組長度進行取余,得到的結果就是存放位置的索引了。但是不同的key可能會得到相同的散列值,也就是哈希沖突的問題,python內部是使用開放尋址的方法來解決的,開放尋址法就不在此詳細說了。關於為什么dict是無序的,這個是因為python內部會保證散列表至少有三分之一的位置為空,當我們增加元素的時候,python有可能會對散列表進行擴容,具體操作就是重新開辟一塊更大的空間,將原有的元素添加到新表里面,這個過程中可能又會發生新的散列沖突,導致新的散列表中的鍵的次序發生變化。當然呢如果想要保存順序也可以使用OrderedDict來處理
dict操作的時間復雜度:
操作 | 操作說明 | 時間復雜度 |
---|---|---|
copy | 復制 | O(n) |
get(value) | 獲取 | O(1) |
set(value) | 修改 | O(1) |
delete(value) | 刪除 | O(1) |
search(in) | 字典搜索 | O(1) |
iterration | 字典迭代 | O(n) |
set集合和dict一樣也是基於散列表的,只是他的表元只包含值的引用而沒有對鍵的引用,其他的和dict基本上是一致的,所以在此就不再多說了。