要理解為什么 Python 代碼在函數中運行得更快,我們需要首先了解 Python 是如何執行代碼的4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
我們知道,python 是一種解釋型語言,它會逐行讀取并執行代碼4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
當運行一個 python 程序的時候,首先將代碼編譯成字節碼(一種更接近機器碼的中間語言)然后 python 解釋器執行字節碼4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
由上所示,python 中的 dis 模塊將函數 hello_world 分解為字節碼4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
需要注意的是,python 解釋器是一個執行字節碼的虛擬機,默認的 python 解釋器是用 C 編寫的,即 CPython4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
還有其他的 python 解釋器如 Jython(用 Java 編寫),IronPython(用于 )和PyPy(用 Python 和 C 編寫)4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
為什么 python 代碼在函數中運行得更快
4p928資訊網——每日最新資訊28at.com
我們來編寫一個簡單的例子:定義一個函數 my_function,函數內部包含一個 for 循環4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
編譯該函數的時候,字節碼可能如下所示4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
這里的關鍵指令是 STORE_FAST ,用于存儲循環變量 i 4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
現在我們把這個 for 循環放在 python 腳本的頂層(全局范圍內),然后再來看一下字節碼4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
可以看到關鍵指令變成了 STORE_NAME,而不是 STORE_FAST 4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
字節碼 STORE_FAST比 STORE_NAME 快,因為在函數中,局部變量存儲在固定長度的數組中,而不是存儲在字典中。這個數組可以通過索引直接訪問,使得變量檢索非常快4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
基本上,它只是一個指向列表的指針,并增加了 PyObject 的引用計數,這兩個都是高效的操作4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
另一方面,全局變量存儲在一個字典。當訪問全局變量時,Python 必須執行哈希表查找,這涉及計算哈希值,然后檢索與之關聯的值4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
雖然經過優化,但仍然比基于索引的查找慢4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
基準測試驗證
4p928資訊網——每日最新資訊28at.com
我們知道在 Python 中,代碼執行的速度取決于代碼執行的位置——在函數中還是在全局作用域中4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
讓我們用一個簡單的基準測試的例子來比較一下4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
首先定義一個求階乘的函數4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
然后在全局范圍內執行相同的代碼4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
為了對這兩段代碼進行基準測試,我們可以在 Python 中使用 timeit 模塊,它提供了一種簡單的方法來對少量 Python 代碼進行計時4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
可以看到,函數代碼的執行速度比全局作用域代碼要快4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
需要注意的是,這兩段代碼最好不要放在同一腳本中,要分開單獨運行4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
這是因為 benchmark() 函數在執行時間上增加了一些開銷,并且全局代碼在內部進行了優化4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
cProfile 分析4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
python 提供了一個 cProfile 內置模塊4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
讓我們用它來分析一個新例子:在局部和全局范圍內計算平方和4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
上面的例子中,可以認為sum_of_squares_g() 函數是全局的,因為它使用了兩個全局變量, i 和 total4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
從性能分析結果中,可以看到函數代碼在執行時間方面比全局更有效4p928資訊網——每日最新資訊28at.com
圖片4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
如何優化 python 函數的性能
4p928資訊網——每日最新資訊28at.com
前面我們知道,Python 代碼在函數中運行往往比在全局范圍內運行要快得多4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
如果想要進一步提高 python 函數代碼效率,不妨考慮一下使用局部變量而不是全局變量4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
另一種方法是盡可能使用內置函數和庫。Python 的內置函數是用 C 實現的,比 Python 快得多4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
比如 NumPy 和 Pandas,也是用 C 或 C++ 實現的,它們比實現同樣功能的 Python 代碼速度更快4p928資訊網——每日最新資訊28at.com
4p928資訊網——每日最新資訊28at.com
又比如同樣是實現數字求和的功能,python 內置的 sum 函數要比你自己編寫函數速度更快4p928資訊網——每日最新資訊28at.com
本文鏈接:http://www.www897cc.com/showinfo-26-10609-0.html為什么 Python 代碼在函數中運行得更快?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 使用二進制字面量在現代C++中的應用
下一篇: 數據組合利器:從入門到精通Python中的zip()函數應用