python list 底层实现的数据结构_Python数据结构大起底——list篇
python內置的多種數據結構為編程提供了相當的便利,靈活的使用python中的內置數據類型可以達到事半功倍的效果,本文是對Python一些常用數據類型的整理,并列舉出來了一些使用技巧。
使用最多的數據結構 list
list內置了許多方法,常用的如:list.append(x)
list.insert(i,x)
list.remove(x)
list.pop(i)
list.index(x, start, end)
list.sort(key, reverse)
list.reverse()
list.copy()
...
上面的方法中,根據字面意思就能知道方法的作用,靈活使用這些方法,可以實現出來其他的數據結構。
使用list實現stack
stack是一個后進先出的數據結構,不理解stack的可以參看我的這篇博客,他的接口一般被這樣定義:
// Stack 接口,java代碼表示public Interface Stack{
// 添加一個元素 public void push(Item item);
// 移除最后添加的元素,并返回這個元素 public Item pop();
// 空監測 public isEmpty();
}
在python的list中,pop()方法依舊存在,使用不帶參數的pop方法,就會從移除list最后一個元素,而push()方法則等價于list的append方法。所以可以直接把list當做stack來使用。
stack = []
stack.append('a') # stack = ['a']
stack.append('b') # stack = ['b']
last = stack.pop() # last='b', stack=['a']
如果你覺得這樣做不是很安全,萬一一不小心調用了別的方法,很容易破壞數據結構,那么你可以使用list自己實現一個Stack(雖然這看上去有點脫褲子放屁):
class Stack:
def __init__(self):
| self._stack = []
def is_empty(self):
return False if self.stack else True
def pop(self):
return self._stack.pop()
def push(self, item):
self._stack.append(item)
def __iter__(self):
for item in self._stack:
yield item
使用list實現queue
queue的特點是先進先出,不理解queue的可以參看我的這篇文章,queue的接口一般包含如下幾種方法:
// Queue接口,java代碼表示public Interface Queue{
// 入隊操作,添加到最右(后) public void enqueue(Item);
//出隊操作,移除最早入隊的(最左邊) public Item dequeue();
public isEmpty();
}
值得慶祝的是,當我們要使用queue的時候并不要自己去編寫一個queque的結構,直接使用python collections模塊里面的deque即可,當然你也可以嘗試自己實現。
deque保留了list的append方法,但是有而外的添加了一個popleft方法,從而實現了dequeque的操作。
from collections import deque
queue = deque()
queue.append(1) # [1]
queue.append(2) # [1, 2]
queue.append(3) # [1, 2, 3]
first = queue.popleft() # first=1, queue=[2, 3]
list Comprehensions 列表推導
python官方文檔列出了三中創建列表的方式,他們分別是迭代器,lambda和列表推導
# 使用迭代去
squares = []
for x in range(10):
squares.append(x**2)
# 使用lambda
squares = list(map(lambda x:x**2, range(10)))
# 使用列表推導
squares = [x**2 for x in range(10)]
列表推導式可以讓代碼變得更加簡潔,解決2SUM問題,使用列表推導只需要一行代碼即可暴力求解:
# 從1到100,兩兩組合,求所有合為105的組合
[(x, y) for x in range(1,101) y in range(1,101) if (x != y and x+y == 105)]
列表推導式還支持嵌套,如生成一個二維數組
# 生成一個5*5的二維數組
[[i for i in range(5)] for col in range(5) ]
del操作
list之所以能夠把很多不同類型的數據放在一個集合里面,其他原因在于python的引用特性,所以對于列表內的任何一個元素都可以直接del。
a = [1, 2, 3, 4, 5]
del a[0] # 可以直接del
del a[2:4] # 也可以按照范圍del,范圍是左閉右開[2:4)
引用的陷阱
[[i for i in range(5)] for col in range(5) ]
這是上面用來創建二維數組的例子,有些比較聰明的人,可能會想到利用python的語言特性,與時代嗎可能會這么寫:
[[i for i in range(5)] * 5]
這看上去很簡潔,并且兩段代碼輸出的內容確實一毛一樣的,但這并不稱得上是聰明,簡直愚蠢。事實上,當你嘗試修改二維數組的任何一列的時候,每一列都會被改變。
a = [[i for i in range(5)] * 5]
# output a:
# [
# [1,2,3,4,5],
# [1,2,3,4,5],
# [1,2,3,4,5],
# [1,2,3,4,5],
# [1,2,3,4,5],
# ]
a[0][0] = 5
# output a:
# [
# [5,2,3,4,5],
# [5,2,3,4,5],
# [5,2,3,4,5],
# [5,2,3,4,5],
# [5,2,3,4,5],
# ]
出現上面巨大bug的原因就是,內存中事實上只創建了a[0],后面的幾列由于使用的是*操作符,都沒有創建新的內存空間,本質上他們是同一塊內存塊。
copy和deepcopy
同樣的問題還會出現在數組拷貝的時候。
a = [[1,2,3], [4,5,6]]
b = a.copy()
a[0][0] = [9]
print(a, b)
上面的代碼使用list的copy方法,把a復制了一份給b,按照字面意思理解,也就是說重新開辟了一塊兒內存空間,并把a的內容copy進去,這樣a和b就完全是兩個獨立的內存空間了。
但事實上上面的代碼,修改了a[0][0]之后,b[0][0]緊跟著也變成了9.這就有點匪夷所思了。這是由于拷貝的深度有限,list默認的copy在一維數組使用并沒有太大問題,但一旦數組內包含了其他深一層的引用,copy只會復制最上層,這樣對于兩塊內存塊來說,再往下一層,本質上還是使用了同一塊內存塊。所以就發生了上面匪夷所思的問題。
解決問題的辦法是使用深拷貝,python 的list并沒有實現deepcopy,但我們可以在copy模塊中找到他。
import copy
a = [[1,2,3], [4,5,6]]
b = copy.deepcopy(a)
然后從最外層到最內層,完完全全的拷貝了a,a與b再也不會有哪一個元素共同同一塊兒內存了。
總結
python對list的實現和功能的設計恰到好處,既不感到臃腫,又絕對夠用,畢竟他是python數據結構的核心。在某些情況下你也可以選擇不使用list,這個時候可以參見python的collections模塊,尋找其他適合你的集合類型。
總結
以上是生活随笔為你收集整理的python list 底层实现的数据结构_Python数据结构大起底——list篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql解释中fitered_MySQ
- 下一篇: oracle display set,C