在 Python 中,有四類最常見的內建容器類型: 列表(list) 元組(tuple) 字典(dict) 集合(set) 。通過單獨或是組合使用它們,可以高效的完成很多事情。

Python 語言自身的內部實現細節也與這些容器類型息息相關。比如 Python 的類實例屬性、全局變量 globals() 等就都是通過字典類型來存儲的。

在這篇文章里,我首先會從容器類型的定義出發,嘗試總結出一些日常編碼的最佳實踐。之后再圍繞各個容器類型提供的特殊機能,分享一些編程的小技巧。

當我們談論容器時,我們在談些什么?

我在前面給了“容器”一個簡單的定義:專門用來裝其他對象的就是容器。但這個定義太寬泛了,無法對我們的日常編程產生什么指導價值。要真正掌握 Python 里的容器,需要分別從兩個層面入手:

  • 底層實現: 內置容器類型使用了什么數據結構?某項操作如何工作?

  • 高層抽象: 什么決定了某個對象是不是容器?哪些行為定義了容器?

下面,讓我們一起站在這兩個不同的層面上,重新認識容器。

底層看容器

Python 是一門高級編程語言, 它所提供的內置容器類型,都是經過高度封裝和抽象后的結果。 和“鏈表”、“紅黑樹”、“哈希表”這些名字相比,所有 Python 內建類型的名字,都只描述了這個類型的功能特點,其他人完全沒法只通過這些名字了解它們的哪怕一丁點內部細節。

這是 Python 編程語言的優勢之一。相比 C 語言這類更接近計算機底層的編程語言,Python 重新設計并實現了對編程者更友好的內置容器類型,屏蔽掉了內存管理等額外工作。為我們提供了更好的開發體驗。

但如果這是 Python 語言的優勢的話,為什么我們還要費勁去了解容器類型的實現細節呢?答案是: 關注細節可以幫助我們編寫出更快的代碼。

如果你依然在編程的世界里迷茫,可以加入我們的Python學習扣qun:784758214,看看前輩們是如何學習的。交流經驗。從基礎的python腳本到web開發、爬蟲、django、數據挖掘等,零基礎到項目實戰的資料都有整理。送給每一位python的小伙伴!分享一些學習的方法和需要注意的小細節,點擊加入我們的 python學習者聚集地

寫更快的代碼

1. 避免頻繁擴充列表/創建新列表

所有的內建容器類型都不限制容量。如果你愿意,你可以把遞增的數字不斷塞進一個空列表,最終撐爆整臺機器的內存。

在 Python 語言的實現細節里,列表的內存是按需分配的[注1],當某個列表當前擁有的內存不夠時,便會觸發內存擴容邏輯。而分配內存是一項昂貴的操作。雖然大部分情況下,它不會對你的程序性能產生什么嚴重的影響。但是當你處理的數據量特別大時,很容易因為內存分配拖累整個程序的性能。

還好,Python 早就意識到了這個問題,并提供了官方的問題解決指引,那就是: “變懶”

如何解釋“變懶”? range() 函數的進化是一個非常好的例子。

在 Python 2 中,如果你調用 range(100000000) ,需要等待好幾秒才能拿到結果,因為它需要返回一個巨大的列表,花費了非常多的時間在內存分配與計算上。但在 Python 3 中,同樣的調用馬上就能拿到結果。因為函數返回的不再是列表,而是一個類型為 range 的懶惰對象,只有在你迭代它、或是對它進行切片時,它才會返回真正的數字給你。

所以說,為了提高性能,內建函數 range “變懶”了。 而為了避免過于頻繁的內存分配,在日常編碼中,我們的函數同樣也需要變懶,這包括:

  • 更多的使用 yield 關鍵字,返回生成器對象

  • 盡量使用生成器表達式替代列表推導表達式

  • 生成器表達式: (iforinrange(100))