『壹』 python中的數據結構分析
1.Python數據結構篇
數據結構篇主要是閱讀[Problem Solving with Python](Welcome to Problem Solving with Algorithms and Data Structures) [該網址鏈接可能會比較慢]時寫下的閱讀記錄,當然,也結合了部分[演算法導論](Introction to Algorithms)
中的內容,此外還有不少wikipedia上的內容,所以內容比較多,可能有點雜亂。這部分主要是介紹了如何使用Python實現常用的一些數據結構,例
如堆棧、隊列、二叉樹等等,也有Python內置的數據結構性能的分析,同時還包括了搜索和排序(在演算法設計篇中會有更加詳細的介紹)的簡單總結。每篇文
章都有實現代碼,內容比較多,簡單演算法一般是大致介紹下思想及演算法流程,復雜的演算法會給出各種圖示和代碼實現詳細介紹。
**這一部分是下
面演算法設計篇的前篇,如果數據結構還不錯的可以直接看演算法設計篇,遇到問題可以回來看數據結構篇中的某個具體內容充電一下,我個人認為直接讀演算法設計篇比
較好,因為大家時間也都比較寶貴,如果你會來讀這些文章說明你肯定有一定基礎了,後面的演算法設計篇中更多的是思想,這里更多的是代碼而已,嘿嘿。**
(1)[搜索](Python Data Structures)
簡述順序查找和二分查找,詳述Hash查找(hash函數的設計以及如何避免沖突)
(2)[排序](Python Data Structures)
簡述各種排序演算法的思想以及它的圖示和實現
(3)[數據結構](Python Data Structures)
簡述Python內置數據結構的性能分析和實現常用的數據結構:棧、隊列和二叉堆
(4)[樹總結](Python Data Structures)
簡述二叉樹,詳述二叉搜索樹和AVL樹的思想和實現
2.Python演算法設計篇
演算法設計篇主要是閱讀[Python Algorithms: Mastering Basic Algorithms in the Python Language](Python Algorithms: Mastering Basic Algorithms in the Python Language)[**點擊鏈接可進入Springer免費下載原書電子版**]之後寫下的讀書總結,原書大部分內容結合了經典書籍[演算法導論](Introction to Algorithms),
內容更加細致深入,主要是介紹了各種常用的演算法設計思想,以及如何使用Python高效巧妙地實現這些演算法,這里有別於前面的數據結構篇,部分演算法例如排
序就不會詳細介紹它的實現細節,而是側重於它內在的演算法思想。這部分使用了一些與數據結構有關的第三方模塊,因為這篇的重點是演算法的思想以及實現,所以並
沒有去重新實現每個數據結構,但是在介紹演算法的同時會分析Python內置數據結構以及第三方數據結構模塊的優缺點,也就意味著該篇比前面都要難不少,但
是我想我的介紹應該還算簡單明了,因為我用的都是比較朴實的語言,並沒有像演算法導論一樣列出一堆性質和定理,主要是對著某個問題一步步思考然後演算法就出來
了,嘿嘿,除此之外,裡面還有很多關於python開發的內容,精彩真的不容錯過!
這里每篇文章都有實現代碼,但是代碼我一般都不會分
析,更多地是分析演算法思想,所以內容都比較多,即便如此也沒有包括原書對應章節的所有內容,因為內容實在太豐富了,所以我只是選擇經典的演算法實例來介紹算
法核心思想,除此之外,還有不少內容是原書沒有的,部分是來自演算法導論,部分是來自我自己的感悟,嘻嘻。該篇對於大神們來說是小菜,請一笑而過,對於菜鳥
們來說可能有點難啃,所以最適合的是和我水平差不多的,對各個演算法都有所了解但是理解還不算深刻的半桶水的程序猿,嘿嘿。
本篇的順序按照原書[Python Algorithms: Mastering Basic Algorithms in the Python Language](Python Algorithms: Mastering Basic Algorithms in the Python Language)的章節來安排的(章節標題部分相同部分不同喲),為了節省時間以及保持原著的原滋原味,部分內容(一般是比較難以翻譯和理解的內容)直接摘自原著英文內容。
**1.
你也許覺得很多內容你都知道嘛,沒有看的必要,其實如果是我的話我也會這么想,但是如果只是歸納一個演算法有哪些步驟,那這個總結也就沒有意義了,我覺得這
個總結的亮點在於想辦法說清楚一個演算法是怎麼想出來的,有哪些需要注意的,如何進行優化的等等,採用問答式的方式讓讀者和我一起來想出某個問題的解,每篇
文章之後都還有一兩道小題練手喲**
**2.你也許還會說演算法導論不是既權威又全面么,基本上每個演算法都還有詳細的證明呢,讀演算法導論豈
不更好些,當然,你如果想讀演算法導論的話我不攔著你,讀完了感覺自己整個人都不好了別怪小弟沒有提醒你喲,嘻嘻嘻,左一個性質右一個定理實在不適合演算法科
普的啦,沒有多少人能夠堅持讀完的。但是碼農與蛇的故事內容不多喲,呵呵呵**
**3.如果你細讀本系列的話我保證你會有不少收獲的,需要看演算法導論哪個部分的地方我會給出提示的,嘿嘿。溫馨提示,前面三節內容都是介紹基礎知識,所以精彩內容從第4節開始喲,么么噠 O(∩_∩)O~**
(1)[Python Algorithms - C1 Introction](Python Algorithms)
本節主要是對原書中的內容做些簡單介紹,說明演算法的重要性以及各章節的內容概要。
(2)[Python Algorithms - C2 The basics](Python Algorithms)
**本節主要介紹了三個內容:演算法漸近運行時間的表示方法、六條演算法性能評估的經驗以及Python中樹和圖的實現方式。**
(3)[Python Algorithms - C3 Counting 101](Python Algorithms)
原書主要介紹了一些基礎數學,例如排列組合以及遞歸循環等,但是本節只重點介紹計算演算法的運行時間的三種方法
(4)[Python Algorithms - C4 Inction and Recursion and Rection](Python Algorithms)
**本節主要介紹演算法設計的三個核心知識:Inction(推導)、Recursion(遞歸)和Rection(規約),這是原書的重點和難點部分**
(5)[Python Algorithms - C5 Traversal](Python Algorithms)
**本節主要介紹圖的遍歷演算法BFS和DFS,以及對拓撲排序的另一種解法和尋找圖的(強)連通分量的演算法**
(6)[Python Algorithms - C6 Divide and Combine and Conquer](Python Algorithms)
**本節主要介紹分治法策略,提到了樹形問題的平衡性以及基於分治策略的排序演算法**
(7)[Python Algorithms - C7 Greedy](Python Algorithms)
**本節主要通過幾個例子來介紹貪心策略,主要包括背包問題、哈夫曼編碼和最小生成樹等等**
(8)[Python Algorithms - C8 Dynamic Programming](Python Algorithms)
**本節主要結合一些經典的動規問題介紹動態規劃的備忘錄法和迭代法這兩種實現方式,並對這兩種方式進行對比**
(9)[Python Algorithms - C9 Graphs](Python Algorithms)
**本節主要介紹圖演算法中的各種最短路徑演算法,從不同的角度揭示它們的內核以及它們的異同**
『貳』 python的性能
PPT的性能,這個你也找找這方面的消息吧,關於這個性能的一些介紹上多了解一下這個情況。
『叄』 【Python 】性能優化系列:隨機數
最近在做的項目重點部分與大量生成隨機數有關,維度高達[1700000,10000],需要生成 10 x 30 次左右,這里遇到內存和速度的雙重瓶頸,特地研究了一下如何優化隨機數。
優化時間測試所需的分析工具在另一篇博客《性能優化系列一:分析工具》中提到。
原生的python中也有隨機模塊生成 random.randint 和 random.random 等,但是速度非常慢,numpy 速度可以大幅提升。一般都採用numpy生成隨機數。
比較常用的就是以上幾種。在需要生成大量隨機數的情況下,或生成偽隨機數的情況下,python 3.7 常用 RandomState 。
直接生成大規模非稀疏矩陣如下,經常遇到 MemoryError 的錯誤,大概是同時生成多個float64精度的大規模隨機矩陣伺服器內存不夠,而random state 似乎也沒提供調整類型的attr,
這時最好使用即使生成即使銷毀,僅保留種子作為索引,同樣,多個CPU之間共享大規模矩陣涉及到共享內存或數據傳輸同步較慢的問題,最好也共享seed而不是直接共享矩陣。
ps. 這里注意一般我們設置time.time()為種子時,對於並發性程序是無效的,不要在並發程序中同時定義,建議生成一個seed list 列表再從中取。
這里可以對大規模矩陣進行分片以進行後續的np 乘法,再切片賦值,以時間換內存。這種情況的麻煩在於如果設定隨機數種子會導致每個分片的隨機數相同。可以利用一個最初seed(爺爺種子)randint生成 一組切片組數的seed(父親種子),再每次從中取不同的隨機數。
在上述切片方法嘗試之後,可以解決內存問題。但是時間非常慢,特別是採取s = 1時在standard normal 上調用170萬次的時間長達3000s,line search一下搜索了大約100000為切片值仍然太慢。在文檔中發現了 BitGenerator 和 Generator ,大約可以提速到原來的 1/3。
除了Numpy和基本模塊之外,AES CTR 加密演算法生成隨機數也很快,但是並不能有比較方便的方式控制每次生成的一樣。參見以下reference。
tensorflow 和 pytorch 也都有大規模生成隨機tensor的方式。性能待考。
1. 超快生成隨機數的方式CSDN博客
2. tensorflow 生成隨機tensor
『肆』 有哪些 Python 經典書籍
《Python編程入門:從入門到實踐》
【同時使用Python 2.X和3.X講解】
Amazon編程入門類榜首圖書,最值得關注的Python入門書
從基本概念到完整項目開發,幫助零基礎讀者迅速掌握Python編程,開發實際項目
這本書分兩部分:
第一部分介紹用Python編程所必須了解的基本概念,包括matplotlib、NumPy和Pygal等強大的Python庫和工具介紹,以及列表、字典、if語句、類、文件與異常、代碼測試等內容;
第二部分將理論付諸實踐,講解如何開發三個項目,包括簡單的Python 2D游戲開發,如何利用數據生成互動式的信息圖,以及創建和定製簡單的Web應用,並幫讀者解決常見編程問題和困惑。
《Python基礎教程(第2版·修訂版)》
【Python 2.5講解,Python 3上也能運行】
各大網店最暢銷的Python入門書
全書分為三部分。
第一部分講述Python語法,沒有廢話,還摻入了一些Python 3.0要注意的細節。
第二部分介紹了常用的GUI、框架等應用,點到即止,算是為第三部分做鋪墊了,從數目眾多的應用中可以了解到Python的強大。
第三部分是Project,全書最大的亮點,大家肯定喜歡。
作者將前面講述的內容應用到10個引人入勝的項目中,並以模板的形式介紹了項目的開發過程,手把手教授Python開發。
《Python語言及其應用》
【Python 3.X】
語言風格輕松詼諧,講解多種Python工具和第三方庫
實例涉及商業、科研以及藝術領域使用Python開發各種應用
亞馬遜最受歡迎的Python編程書之一,評分4.5
書中首先介紹了Python的基礎知識,然後逐漸深入多種主題,結合教程和攻略式風格來講解Python 3中的概念。每章結尾的練習可以幫助你鞏固所學的知識。
本書會為你學習Python打下堅實的基礎,包括測試、調試、代碼復用的最佳實踐以及其他開發技巧。
《Python編程入門(第3版)》
【Python 3.X 】
從算術運算、字元串、變數,到函數、數據結構、輸入輸出和異常處理,應有盡有
《父與子的編程之旅:與小卡特一起學Python》
【Python 2.X 】
原版Amazon 最受歡迎的青少年編程圖書
最簡單易學的內容組織方式,老少皆宜
第一版獲Jolt大獎
本書中,Warren和Carter父子以親切的筆調、通俗的語言,透徹全面地介紹了計算機編程世界。
他們以簡單易學的Python語言為例,通過可愛的漫畫、有趣的例子,生動地介紹了變數、循環、輸入和輸出、數據結構以及圖形用戶界面等編程的基本概念。
只要懂得計算機的基本操作,如啟動程序、保存文件,任何人都可以跟隨本書,由簡入難,學會編寫程序,甚至製作游戲。
本書內容經過教育專家的評審,經過孩子的親身檢驗,並得到了家長的認可。
《編程導論》
【Python 2.7】
以麻省理工學院開放式課程(OpenCourseWare)中最受歡迎的計算機科學課程為基礎,旨在培養讀者的編程思維,使讀者擁有計算機科學家的視野
本書涵蓋了Python的大部分特性,重點介紹如何使用Python這門語言,共包含編程基礎、Python程序設計語言、理解計算的關鍵概念、計算問題的解決技術等四個方面。
本書將Python語言特性和編程方法貫穿全書,目的是幫助讀者在學習Python的同時掌握如何使用計算來解決有趣的問題。
《流暢的Python》
【兼顧Python 3和Python 2】
PSF研究員、知名PyCon演講者心血之作
Python核心開發人員擔綱技術審校
全面深入,對Python語言關鍵特性剖析到位
大量詳盡代碼示例,並附有主題相關高質量參考文獻和視頻鏈接
本書致力於幫助Python開發人員挖掘這門語言及相關程序庫的優秀特性,避免重復勞動,同時寫出簡潔、流暢、易讀、易維護,並且具有地道Python風格的代碼。本書尤其深入探討了Python語言的高級用法,涵蓋數據結構、Python風格的對象、並行與並發,以及元編程等不同的方面。
《Python項目開發實戰(第2版)》
【Python 2.7】
網羅Python項目開發中的流程,讓你的編程事半功倍
Python項目與封裝/團隊開發環境/問題驅動開發/源碼管理(Mercurial) Jenkins持續集成(CI)/環境搭建與部署的自動化(Ansible)/Django框架……
這是一本偏工程的圖書,沒怎麼講Python語言基礎知識,直接告訴你怎麼搭建開發環境,做好代碼管理和文檔管理以及缺陷管理等工作。
《Python網路編程攻略》
【Python 2.7】
可作為任何一門網路編程課程中培養實踐技能的補充材料
需要讀者對Python語言及TCP/IP等基本的網路概念有了解,但即使不精通也能通過本書理解相關概念
本書全面介紹了Python網路編程涉及的重要問題,包括網路編程、系統和網路管理、網路監控以及Web應用開發。作者通過70多篇攻略,清晰簡明地描述了各種網路任務和問題,提出了可用於多種場景的解決方案,並細致地分析了整個操作過程。
《Python網路編程(第3版)》
【Python 3.X】
涵蓋網路編程所有經典話題,提供大量代碼清單及示例
從應用開發角度介紹網路編程基本概念、模塊以及第三方庫
本書針對想要深入理解使用Python來解決網路相關問題或是構建網路應用程序的技術人員,結合實例講解了網路協議、網路數據及錯誤、電子郵件、伺服器架構和HTTP及Web應用程序等經典話題。
具體內容包括:全面介紹Python3中最新提供的SSL支持,非同步I/O循環的編寫,用Flask框架在Python代碼中配置URL,跨站腳本以及跨站請求偽造攻擊網站的原理及保護方法,等等。
《Python性能分析與優化》
【Python 2.X】
全面掌握Python代碼性能分析和優化方法
消除性能瓶頸,迅速改善程序性能
本書首先介紹什麼是性能分析,性能分析如何在項目開發周期中發揮作用,以及通過在項目中進行性能分析實踐能夠取得的效果。
緊接著介紹分析性能所需的核心工具(性能分析器和可視化性能分析器)。
然後介紹一系列性能優化技術,最後一章會介紹一個具有實際意義的優化案例。
《精通Python設計模式》
【Python 3.X】
用現實例子展示各模式的關鍵特性
16種基本設計模式,輕松解決軟體設計常見問題
本書分三部分,共16章介紹一些常用的設計模式。
第一部分介紹處理對象創建的設計模式,包括工廠模式、建造者模式、原型模式;
第二部分介紹處理一個系統中不同實體(類、對象等)之間關系的設計模式,包括外觀模式、享元模式等;
第三部分介紹處理系統實體之間通信的設計模式,包括責任鏈模式、觀察者模式等。
《Flask Web開發:基於Python的Web應用開發實戰》
【Python 2.7和3.3】
從安裝與環境設置講起,一步一步搭建伺服器端Web應用
全流程講解Web應用開發,給出最佳實踐
本書共分三部分,全面介紹如何基於Python微框架Flask進行Web開發。
第一部分是Flask簡介,介紹使用Flask框架及擴展開發Web程序的必備基礎知識。
第二部分則給出一個實例,真正帶領大家一步步開發完整的博客和社交應用Flasky,從而將前述知識融會貫通,付諸實踐。
第三部分介紹了發布應用之前必須考慮的事項,如單元測試策略、性能分析技術、Flask程序的部署方式等。
《Python Web開發:測試驅動方法》
【(Django、Selenium)相關部分使用Python 3.3講解】
亞馬遜4.8星評好書
實戰式TDD開發指南,使用Django等流行框架開發現代Web應用!
學習Django、Selenium、Git、jQuery和Mock,以及其他當前流行Web開發技術
「這本書很棒、很有趣,所講的全都是重點知識。如果有人想用Python做測試、學習Django或者想使用Selenium,我極力推薦這本書。要使開發者保持頭腦清醒,測試可謂至關重要。Harry完成了一項不可思議的工作,他不僅吸引了我們對測試的關注,而且還探索了切實可行的測試實踐方案。」
——Michael Foord,Python核心開發者,unittest維護者
《數據科學入門》
【Python 2.7】
Google數據科學家、軟體工程師Joel Grus作品
用Python從零開始講解數據科學的重量級讀本
數據科學、機器學習、模式識別領域必備
本書從零開始講解數據科學。
具體內容包括Python簡介,可視化數據,線性代數,統計,概率,假設與推斷,梯度下降法,如何獲取數據,k近鄰法,樸素貝葉斯演算法等。
作者藉助大量具體例子以及數據挖掘、統計學、機器學習等領域的重要概念,通過講解基礎數據科學工具和演算法實現,帶你快速跨入數據科學大門。
書中含大量數據科學領域的庫、框架、模塊和工具包。
《機器學習實戰》
【Python 2.7】
最暢銷機器學習圖書
介紹並實現機器學習的主流演算法
面向日常任務的高效實戰內容
全書通過精心編排的實例,切入日常工作任務,摒棄學術化語言,利用高效的可復用Python代碼來闡釋如何處理統計數據,進行數據分析及可視化。
通過各種實例,讀者可從中學會機器學習的核心演算法,並能將其運用於一些策略性任務中,如分類、預測、推薦。另外,還可用它們來實現一些更高級的功能,如匯總和簡化等。
《機器學習系統設計》
【Python 2.7及以上】
微軟Bing核心團隊成員推出
聚焦演算法編寫和編程方式
結合大量實例學會解決實際問題
本書將向讀者展示如何從原始數據中發現模式,首先從Python與機器學習的關系講起,再介紹一些庫,然後就開始基於數據集進行比較正式的項目開發了,涉及建模、推薦及改進,以及聲音與圖像處理。通過流行的開源庫,我們可以掌握如何高效處理文本、圖片和聲音。同時,讀者也能掌握如何評估、比較和選擇適用的機器學習技術。
《Python數據處理》
【Python 2.7】
將數據處理過程自動化!
全面掌握用Python進行爬蟲抓取以及數據清洗與分析的方法,輕松實現高效數據處理!
本書採用基於項目的方法,介紹用Python完成數據獲取、數據清洗、數據探索、數據呈現、數據規模化和自動化的過程。
主要內容包括:Python基礎知識,如何從CSV、Excel、XML、JSON和PDF文件中提取數據,如何獲取與存儲數據,各種數據清洗與分析技術,數據可視化方法,如何從網站和API中提取數據。
《Python數據分析基礎教程:NumPy學習指南(第2版)》
【Python 2.7】
NumPy中文入門教程,Python數據分析首選
從最基礎的知識講起,手把手帶你進入大數據挖掘領域
囊括大量具有啟發性與實用價值的實戰案例
本書從NumPy安裝講起,逐漸過渡到數組對象、常用函數、矩陣運算、線性代數、金融函數、窗函數、質量控制等內容,致力於向初中級Python編程人員全面講述NumPy及其使用。
另外,通過書中豐富的示例,你還將學會Matplotlib繪圖,並結合使用其他Python科學計算庫(如SciPy和Scikits),讓工作更有成效,讓代碼更加簡潔而高效。
《Python數據挖掘入門與實踐》
【Python 3.4】
全面釋放Python的數據分析能力
掌握大數據時代核心技術,輕松入門數據挖掘技術並將其應用於實際項目
本書使用簡單易學且擁有豐富第三方庫和良好社區氛圍的Python語言,由淺入深,以真實數據作為研究對象,真刀實槍地向讀者介紹Python數據挖掘的實現方法。通過本書,讀者將邁入數據挖掘的殿堂,透徹理解數據挖掘基礎知識,掌握解決數據挖掘實際問題的最佳實踐!
《Python科學計算基礎教程》
【Python 2.7及以上】
精彩案例展示Numpy等科學計算模塊的強大功能和廣泛應用
剖析Python關於並行與大數據計算的方法
總結科學計算的任務、難點以及最佳實踐經驗
本書是將Python用於科學計算的實用指南,既介紹了相關的基礎知識,又提供了豐富的精彩案例,並為讀者總結了最佳實踐經驗。
其主要內容包括:科學計算的基本概念與選擇Python的理由,科學工作流和科學計算的結構,科學項目相關數據的各個方面,用於科學計算的API和工具包,如何利用Python的NumPy和SciPy包完成數值計算,用Python做符號計算,數據分析與可視化,並行與大規模計算,等等。
《Python數據分析實戰》
【Python 2.X】
了解Python在信息處理、管理和檢索方面的強大功能
學會如何利用Python及其衍生工具處理、分析數據
三個真實Python數據分析案例,將理論付諸實踐
《Python數據分析實戰》展示了如何利用Python 語言的強大功能,以最小的編程代價進行數據的提取、處理和分析,主要內容包括:數據分析和Python 的基本介紹,NumPy 庫,pandas 庫,如何使用pandas 讀寫和提取數據,用matplotlib 庫和scikit-learn 庫分別實現數據可視化和機器學習,以實例演示如何從原始數據獲得信息、D3 庫嵌入和手寫體數字的識別。
《Python網路數據採集》
【Python 3.X】
原書4.6星好評,一本書搞定數據採集
涵蓋數據抓取、數據挖掘和數據分析
提供詳細代碼示例,快速解決實際問題
本書介紹網路數據採集,並為採集新式網路中的各種數據類型提供了全面的指導。
第一部分重點介紹網路數據採集的基本原理:如何用Python從網路伺服器請求信息,如何對伺服器的響應進行基本處理,以及如何以自動化手段與網站進行交互。
第二部分介紹如何用網路爬蟲測試網站,自動化處理,以及如何通過更多的方式接入網路。
《Python計算機視覺編程》
【Python 2.6及以上】
亞馬遜計算機視覺類圖書No.1
詳細剖析多種計算機視覺工具
大量示例極易上手
本書是計算機視覺編程的權威實踐指南,通過Python語言講解了基礎理論與演算法,並通過大量示例細致分析了對象識別、基於內容的圖像搜索、光學字元識別、光流法、跟蹤、3D重建、立體成像、增強現實、姿態估計、全景創建、圖像分割、降噪、圖像分組等技術。
『伍』 python向nas上寫入數據很慢
Python向NAS上寫入數據很慢可能有以下原因:
網路帶寬限制:如果您的NAS和Python程序運行的計算機之間的網路帶寬較低,數據傳輸速度就會變慢。
硬碟讀寫速度限制:如果您的NAS硬碟讀寫速度較慢,或者硬碟已經接近滿負荷,寫入數據的速度就會變慢。
Python程序的性能問題:如果您的Python程序寫入數據的方式不夠高效,或者存在其他性能問題,也會導凳此猜致寫入數據的速度變慢。
針對這些問題,您可以嘗試以下解決方案:
檢查網路帶寬:可以使用網路測試工具檢查NAS和Python程序運行的計算機之間的網路帶寬,如果網路棗型帶寬較低,可以考慮升級網路設備或者使用其他網路傳輸方式。
檢查NAS硬碟讀寫速度:可以使用硬碟測試工具檢查NAS硬碟的讀寫速度,如果硬碟讀寫速度較慢,可以考慮升級硬碟或者使用其他存儲設備。
優化Python程序:可以使用Python性能分析工具檢查Python程序的性能問題,並進行優化。例如扒行,可以使用多線程或者非同步IO等方式提高寫入數據的效率。
總之,Python向NAS上寫入數據很慢可能有多種原因,需要根據具體情況進行分析和解決。
『陸』 如何進行python性能分析
使用time工具粗糙定時
首先,我們可以使用快速然而粗糙的工具:古老的unix工具time,橋基來為我們的代碼檢測運行時間。
1 $ time python yourprogram.py
2
3 real 0m1.028s
4 user 0m0.001s
5 sys 0m0.003s
上面三個輸入變數的意義在文章 stackoverflow article 中有詳細介紹。簡單的說:
real - 表示實際的程序運行時間
user - 表示程序輪消數在用戶態的cpu總時間
sys - 表示在內核態的cpu總時間
通過sys和user時間的求和,你可以直觀的得到系統上沒有其他程序運行時你的程序運行所需要的CPU周期。
若sys和user時間之和遠遠少於real時間,那麼你可以猜測你的程序的主要性能問題很可能與IO等待相關。
使用計時上下文管理器進行細粒度計時
我們的下一個技術涉及訪問細粒度計時信息的直接代碼指令。這是一小段代碼,我發現使用專門的計時測量是非常重要的:
timer.py
01 import time
02
03 class Timer(object):
04 def __init__(self, verbose=False):
05 self.verbose = verbose
06
07 def __enter__(self):
08 self.start = time.time()
09 return self
10
11 def __exit__(self, *args):
12 self.end = time.time()
13 self.secs = self.end - self.start
14 self.msecs = self.secs * 1000 # millisecs
15 if self.verbose:
16 print 'elapsed time: %f ms' % self.msecs
為了使用它,你需要用Python的with關鍵字和Timer上下文管理器包裝想要計時的代碼塊。它將會在你的代碼塊開始執行的時候啟動計時器,在你的代碼塊結束的時候停止計時器。
這是一個使用上述代碼片段的例子:
01 from timer import Timer
02 from redis import Redis
03 rdb = Redis()
04
05 with Timer() as t:
06 rdb.lpush("foo", "bar")
07 print "=> elasped lpush: %s s" % t.secs
08
09 with Timer as t:
10 rdb.lpop("foo")
11 print "=> elasped lpop: %s s" % t.secs
我經常將這些計時器的輸出記錄到文件中,這樣就可以觀察我的程序的性能如何隨著時間進化。
使用分析器逐行統計時間和執行頻率
Robert Kern有一個稱作line_profiler的不錯的項目,我經常使用它查看我的腳步中每行代碼多快多頻繁的被執行。
想要使用它,你需要通過pip安裝該python包:
1 $ pip install line_profiler
一旦安裝完成,你將會使用一個稱做「line_profiler」的新模組和一個臘首「kernprof.py」可執行腳本。
想要使用該工具,首先修改你的源代碼,在想要測量的函數上裝飾@profile裝飾器。不要擔心,你不需要導入任何模組。kernprof.py腳本將會在執行的時候將它自動地注入到你的腳步的運行時。
primes.py
01 @profile
02 def primes(n):
03 if n==2:
04 return [2]
05 elif n<2:
06 return []
07 s=range(3,n+1,2)
08 mroot = n ** 0.5
09 half=(n+1)/2-1
10 i=0
11 m=3
12 while m <= mroot:
13 if s[i]:
14 j=(m*m-3)/2
15 s[j]=0
16 while j
17 s[j]=0
18 j+=m
19 i=i+1
20 m=2*i+3
21 return [2]+[x for x in s if x]
22 primes(100)
一旦你已經設置好了@profile裝飾器,使用kernprof.py執行你的腳步。
1 $ kernprof.py -l -v fib.py
-l選項通知kernprof注入@profile裝飾器到你的腳步的內建函數,-v選項通知kernprof在腳本執行完畢的時候顯示計時信息。上述腳本的輸出看起來像這樣:
01 Wrote profile results to primes.py.lprof
02 Timer unit: 1e-06 s
03
04 File: primes.py
05 Function: primes at line 2
06 Total time: 0.00019 s
07
08 Line # Hits Time Per Hit % Time Line Contents
09 ==============================================================
10 2 @profile
11 3 def primes(n):
12 4 1 2 2.0 1.1 if n==2:
13 5 return [2]
14 6 1 1 1.0 0.5 elif n<2:
15 7 return []
16 8 1 4 4.0 2.1 s=range(3,n+1,2)
17 9 1 10 10.0 5.3 mroot = n ** 0.5
18 10 1 2 2.0 1.1 half=(n+1)/2-1
19 11 1 1 1.0 0.5 i=0
20 12 1 1 1.0 0.5 m=3
21 13 5 7 1.4 3.7 while m <= mroot:
22 14 4 4 1.0 2.1 if s[i]:
23 15 3 4 1.3 2.1 j=(m*m-3)/2
24 16 3 4 1.3 2.1 s[j]=0
25 17 31 31 1.0 16.3 while j
26 18 28 28 1.0 14.7 s[j]=0
27 19 28 29 1.0 15.3 j+=m
28 20 4 4 1.0 2.1 i=i+1
29 21 4 4 1.0 2.1 m=2*i+3
30 22 50 54 1.1 28.4 return [2]+[x for x in s if x]
尋找具有高Hits值或高Time值的行。這些就是可以通過優化帶來最大改善的地方。
程序使用了多少內存?
現在我們對計時有了較好的理解,那麼讓我們繼續弄清楚程序使用了多少內存。我們很幸運,Fabian Pedregosa模仿Robert Kern的line_profiler實現了一個不錯的內存分析器。
首先使用pip安裝:
1 $ pip install -U memory_profiler
2 $ pip install psutil
(這里建議安裝psutil包,因為它可以大大改善memory_profiler的性能)。
就像line_profiler,memory_profiler也需要在感興趣的函數上面裝飾@profile裝飾器:
1 @profile
2 def primes(n):
3 ...
4 ...
想要觀察你的函數使用了多少內存,像下面這樣執行:
1 $ python -m memory_profiler primes.py
一旦程序退出,你將會看到看起來像這樣的輸出:
01 Filename: primes.py
02
03 Line # Mem usage Increment Line Contents
04 ==============================================
05 2 @profile
06 3 7.9219 MB 0.0000 MB def primes(n):
07 4 7.9219 MB 0.0000 MB if n==2:
08 5 return [2]
09 6 7.9219 MB 0.0000 MB elif n<2:
10 7 return []
11 8 7.9219 MB 0.0000 MB s=range(3,n+1,2)
12 9 7.9258 MB 0.0039 MB mroot = n ** 0.5
13 10 7.9258 MB 0.0000 MB half=(n+1)/2-1
14 11 7.9258 MB 0.0000 MB i=0
15 12 7.9258 MB 0.0000 MB m=3
16 13 7.9297 MB 0.0039 MB while m <= mroot:
17 14 7.9297 MB 0.0000 MB if s[i]:
18 15 7.9297 MB 0.0000 MB j=(m*m-3)/2
19 16 7.9258 MB -0.0039 MB s[j]=0
20 17 7.9297 MB 0.0039 MB while j
21 18 7.9297 MB 0.0000 MB s[j]=0
22 19 7.9297 MB 0.0000 MB j+=m
23 20 7.9297 MB 0.0000 MB i=i+1
24 21 7.9297 MB 0.0000 MB m=2*i+3
25 22 7.9297 MB 0.0000 MB return [2]+[x for x in s if x]
line_profiler和memory_profiler的IPython快捷方式
memory_profiler和line_profiler有一個鮮為人知的小竅門,兩者都有在IPython中的快捷命令。你需要做的就是在IPython會話中輸入以下內容:
1 %load_ext memory_profiler
2 %load_ext line_profiler
在這樣做的時候你需要訪問魔法命令%lprun和%mprun,它們的行為類似於他們的命令行形式。主要區別是你不需要使用@profiledecorator來修飾你要分析的函數。只需要在IPython會話中像先前一樣直接運行分析:
1 In [1]: from primes import primes
2 In [2]: %mprun -f primes primes(1000)
3 In [3]: %lprun -f primes primes(1000)
這樣可以節省你很多時間和精力,因為你的源代碼不需要為使用這些分析命令而進行修改。
內存泄漏在哪裡?
cPython解釋器使用引用計數做為記錄內存使用的主要方法。這意味著每個對象包含一個計數器,當某處對該對象的引用被存儲時計數器增加,當引用被刪除時計數器遞減。當計數器到達零時,cPython解釋器就知道該對象不再被使用,所以刪除對象,釋放佔用的內存。
如果程序中不再被使用的對象的引用一直被佔有,那麼就經常發生內存泄漏。
查找這種「內存泄漏」最快的方式是使用Marius Gedminas編寫的objgraph,這是一個極好的工具。該工具允許你查看內存中對象的數量,定位含有該對象的引用的所有代碼的位置。
『柒』 後端編程Python3-調試、測試和性能剖析(下)
單元測試(Unit Testing)
為程序編寫測試——如果做的到位——有助於減少bug的出現,並可以提高我們對程序按預期目標運行的信心。通常,測試並不能保證正確性,因為對大多數程序而言, 可能的輸入范圍以及可能的計算范圍是如此之大,只有其中最小的一部分能被實際地進 行測試。盡管如此,通過仔細地選擇測試的方法和目標,可以提高代碼的質量。
大量不同類型的測試都可以進行,比如可用性測試、功能測試以及整合測試等。這里, 我們只講單元測試一對單獨的函數、類與方法進行測試,確保其符合預期的行為。
TDD的一個關鍵點是,當我們想添加一個功能時——比如為類添加一個方法—— 我們首次為其編寫一個測試用例。當然,測試將失敗,因為我們還沒有實際編寫該方法。現在,我們編寫該方法,一旦方法通過了測試,就可以返回所有測試,確保我們新添加的代碼沒有任何預期外的副作用。一旦所有測試運行完畢(包括我們為新功能編寫的測試),就可以對我們的代碼進行檢查,並有理有據地相信程序行為符合我們的期望——當然,前提是我們的測試是適當的。
比如,我們編寫了一個函數,該函數在特定的索引位置插入一個字元串,可以像下面這樣開始我們的TDD:
def insert_at(string, position, insert):
"""Returns a of string with insert inserted at the position
>>> string = "ABCDE"
>>> result =[]
>>> for i in range(-2, len(string) + 2):
... result.append(insert_at(string, i,「-」))
>>> result[:5]
['ABC-DE', 'ABCD-E', '-ABCDE','A-BCDE', 'AB-CDE']
>>> result[5:]
['ABC-DE', 'ABCD-E', 'ABCDE-', 'ABCDE-']
"""
return string
對不返回任何參數的函數或方法(通常返回None),我們通常賦予其由pass構成的一個suite,對那些返回值被試用的,我們或者返回一個常數(比如0),或者某個不變的參數——這也是我們這里所做的。(在更復雜的情況下,返回fake對象可能更有用一一對這樣的類,提供mock對象的第三方模塊是可用的。)
運行doctest時會失敗,並列出每個預期內的字元串('ABCD-EF'、'ABCDE-F' 等),及其實際獲取的字元串(所有的都是'ABCD-EF')。一旦確定doctest是充分的和正確的,就可以編寫該函數的主體部分,在本例中只是簡單的return string[:position] + insert+string[position:]。(如果我們編寫的是 return string[:position] + insert,之後復制 string [:position]並將其粘貼在末尾以便減少一些輸入操作,那麼doctest會立即提示錯誤。)
Python的標准庫提供了兩個單元測試模塊,一個是doctest,這里和前面都簡單地提到過,另一個是unittest。此外,還有一些可用於Python的第三方測試工具。其中最著名的兩個是nose (code.google.com/p/python-nose)與py.test (codespeak.net/py/dist/test/test.html), nose 致力於提供比標準的unittest 模塊更廣泛的功能,同時保持與該模塊的兼容性,py.test則採用了與unittest有些不同的方法,試圖盡可能消除樣板測試代碼。這兩個第三方模塊都支持測試發現,因此沒必要寫一個總體的測試程序——因為模塊將自己搜索測試程序。這使得測試整個代碼樹或某一部分 (比如那些已經起作用的模塊)變得很容易。那些對測試嚴重關切的人,在決定使用哪個測試工具之前,對這兩個(以及任何其他有吸引力的)第三方模塊進行研究都是值 得的。
創建doctest是直截了當的:我們在模塊中編寫測試、函數、類與方法的docstrings。 對於模塊,我們簡單地在末尾添加了 3行:
if __name__ =="__main__":
import doctest
doctest.testmod()
在程序內部使用doctest也是可能的。比如,blocks.py程序(其模塊在後面)有自己函數的doctest,但以如下代碼結尾:
if __name__== "__main__":
main()
這里簡單地調用了程序的main()函數,並且沒有執行程序的doctest。要實驗程序的 doctest,有兩種方法。一種是導入doctest模塊,之後運行程序---比如,在控制台中輸 入 python3 -m doctest blocks.py (在 Wndows 平台上,使用類似於 C:Python3 lpython.exe 這樣的形式替代python3)。如果所有測試運行良好,就沒有輸出,因此,我們可能寧願執行python3-m doctest blocks.py-v,因為這會列出每個執行的doctest,並在最後給出結果摘要。
另一種執行doctest的方法是使用unittest模塊創建單獨的測試程序。在概念上, unittest模塊是根據Java的JUnit單元測試庫進行建模的,並用於創建包含測試用例的測試套件。unittest模塊可以基於doctests創建測試用例,而不需要知道程序或模塊包含的任何事物——只要知道其包含doctest即可。因此,為給blocks.py程序製作一個測試套件,我們可以創建如下的簡單程序(將其稱為test_blocks.py):
import doctest
import unittest
import blocks
suite = unittest.TestSuite()
suite.addTest(doctest.DocTestSuite(blocks))
runner = unittest.TextTestRunner()
print(runner.run(suite))
注意,如果釆用這種方法,程序的名稱上會有一個隱含的約束:程序名必須是有效的模塊名。因此,名為convert-incidents.py的程序的測試不能寫成這樣。因為import convert-incidents不是有效的,在Python標識符中,連接符是無效的(避開這一約束是可能的,但最簡單的解決方案是使用總是有效模塊名的程序文件名,比如,使用下劃線替換連接符)。這里展示的結構(創建一個測試套件,添加一個或多個測試用例或測試套件,運行總體的測試套件,輸出結果)是典型的機遇unittest的測試。運行時,這一特定實例產生如下結果:
...
.............................................................................................................
Ran 3 tests in 0.244s
OK
每次執行一個測試用例時,都會輸出一個句點(因此上面的輸出最前面有3個句點),之後是一行連接符,再之後是測試摘要(如果有任何一個測試失敗,就會有更多的輸出信息)。
如果我們嘗試將測試分離開(典型情況下是要測試的每個程序和模塊都有一個測試用例),就不要再使用doctests,而是直接使用unittest模塊的功能——尤其是我們習慣於使用JUnit方法進行測試時ounittest模塊會將測試分離於代碼——對大型項目(測試編寫人員與開發人員可能不一致)而言,這種方法特別有用。此外,unittest單元測試編寫為獨立的Python模塊,因此,不會像在docstring內部編寫測試用例時受到兼容性和明智性的限制。
unittest模塊定義了 4個關鍵概念。測試夾具是一個用於描述創建測試(以及用完之後將其清理)所必需的代碼的術語,典型實例是創建測試所用的一個輸入文件,最後刪除輸入文件與結果輸出文件。測試套件是一組測試用例的組合。測試用例是測試的基本單元—我們很快就會看到實例。測試運行者是執行一個或多個測試套件的對象。
典型情況下,測試套件是通過創建unittest.TestCase的子類實現的,其中每個名稱 以「test」開頭的方法都是一個測試用例。如果我們需要完成任何創建操作,就可以在一個名為setUp()的方法中實現;類似地,對任何清理操作,也可以實現一個名為 tearDown()的方法。在測試內部,有大量可供我們使用的unittest.TestCase方法,包括 assertTrue()、assertEqual()、assertAlmostEqual()(對於測試浮點數很有用)、assertRaises() 以及更多,還包括很多對應的逆方法,比如assertFalse()、assertNotEqual()、failIfEqual()、 failUnlessEqual ()等。
unittest模塊進行了很好的歸檔,並且提供了大量功能,但在這里我們只是通過一 個非常簡單的測試套件來感受一下該模塊的使用。這里將要使用的實例,該練習要求創建一個Atomic模塊,該模塊可以用作一 個上下文管理器,以確保或者所有改變都應用於某個列表、集合或字典,或者所有改變都不應用。作為解決方案提供的Atomic.py模塊使用30行代碼來實現Atomic類, 並提供了 100行左右的模塊doctest。這里,我們將創建test_Atomic.py模塊,並使用 unittest測試替換doctest,以便可以刪除doctest。
在編寫測試模塊之前,我們需要思考都需要哪些測試。我們需要測試3種不同的數據類型:列表、集合與字典。對於列表,需要測試的是插入項、刪除項或修改項的值。對於集合,我們必須測試向其中添加或刪除一個項。對於字典,我們必須測試的是插入一個項、修改一個項的值、刪除一個項。此外,還必須要測試的是在失敗的情況下,不會有任何改變實際生效。
結構上看,測試不同數據類型實質上是一樣的,因此,我們將只為測試列表編寫測試用例,而將其他的留作練習。test_Atomic.py模塊必須導入unittest模塊與要進行測試的Atomic模塊。
創建unittest文件時,我們通常創建的是模塊而非程序。在每個模塊內部,我們定義一個或多個unittest.TestCase子類。比如,test_Atomic.py模塊中僅一個單獨的 unittest-TestCase子類,也就是TestAtomic (稍後將對其進行講解),並以如下兩行結束:
if name == "__main__":
unittest.main()
這兩行使得該模塊可以單獨運行。當然,該模塊也可以被導入並從其他測試程序中運行——如果這只是多個測試套件中的一個,這一點是有意義的。
如果想要從其他測試程序中運行test_Atomic.py模塊,那麼可以編寫一個與此類似的程序。我們習慣於使用unittest模塊執行doctests,比如:
import unittest
import test_Atomic
suite = unittest.TestLoader().loadTestsFromTestCase(test_Atomic.TestAtomic)
runner = unittest.TextTestRunner()
pnnt(runner.run(suite))
這里,我們已經創建了一個單獨的套件,這是通過讓unittest模塊讀取test_Atomic 模塊實現的,並且使用其每一個test*()方法(本實例中是test_list_success()、test_list_fail(),稍後很快就會看到)作為測試用例。
我們現在將查看TestAtomic類的實現。對通常的子類(不包括unittest.TestCase 子類),不怎麼常見的是,沒有必要實現初始化程序。在這一案例中,我們將需要建立 一個方法,但不需要清理方法,並且我們將實現兩個測試用例。
def setUp(self):
self.original_list = list(range(10))
我們已經使用了 unittest.TestCase.setUp()方法來創建單獨的測試數據片段。
def test_list_succeed(self):
items = self.original_list[:]
with Atomic.Atomic(items) as atomic:
atomic.append(1999)
atomic.insert(2, -915)
del atomic[5]
atomic[4]= -782
atomic.insert(0, -9)
self.assertEqual(items,
[-9, 0, 1, -915, 2, -782, 5, 6, 7, 8, 9, 1999])
def test_list_fail(self):
items = self.original_list[:]
with self.assertRaises(AttributeError):
with Atomic.Atomic(items) as atomic:
atomic.append(1999)
atomic.insert(2, -915)
del atomic[5]
atomic[4] = -782
atomic.poop() # Typo
self.assertListEqual(items, self.original_list)
這里,我們直接在測試方法中編寫了測試代碼,而不需要一個內部函數,也不再使用unittest.TestCase.assertRaised()作為上下文管理器(期望代碼產生AttributeError)。 最後我們也使用了 Python 3.1 的 unittest.TestCase.assertListEqual()方法。
正如我們已經看到的,Python的測試模塊易於使用,並且極為有用,在我們使用 TDD的情況下更是如此。它們還有比這里展示的要多得多的大量功能與特徵——比如,跳過測試的能力,這有助於理解平台差別——並且這些都有很好的文檔支持。缺失的一個功能——但nose與py.test提供了——是測試發現,盡管這一特徵被期望在後續的Python版本(或許與Python 3.2—起)中出現。
性能剖析(Profiling)
如果程序運行很慢,或者消耗了比預期內要多得多的內存,那麼問題通常是選擇的演算法或數據結構不合適,或者是以低效的方式進行實現。不管問題的原因是什麼, 最好的方法都是准確地找到問題發生的地方,而不只是檢査代碼並試圖對其進行優化。 隨機優化會導致引入bug,或者對程序中本來對程序整體性能並沒有實際影響的部分進行提速,而這並非解釋器耗費大部分時間的地方。
在深入討論profiling之前,注意一些易於學習和使用的Python程序設計習慣是有意義的,並且對提高程序性能不無裨益。這些技術都不是特定於某個Python版本的, 而是合理的Python程序設計風格。第一,在需要只讀序列時,最好使用元組而非列表; 第二,使用生成器,而不是創建大的元組和列表並在其上進行迭代處理;第三,盡量使用Python內置的數據結構 dicts、lists、tuples 而不實現自己的自定義結構,因為內置的數據結構都是經過了高度優化的;第四,從小字元串中產生大字元串時, 不要對小字元串進行連接,而是在列表中累積,最後將字元串列表結合成為一個單獨的字元串;第五,也是最後一點,如果某個對象(包括函數或方法)需要多次使用屬性進行訪問(比如訪問模塊中的某個函數),或從某個數據結構中進行訪問,那麼較好的做法是創建並使用一個局部變數來訪問該對象,以便提供更快的訪問速度。
Python標准庫提供了兩個特別有用的模塊,可以輔助調査代碼的性能問題。一個是timeit模塊——該模塊可用於對一小段Python代碼進行計時,並可用於諸如對兩個或多個特定函數或方法的性能進行比較等場合。另一個是cProfile模塊,可用於profile 程序的性能——該模塊對調用計數與次數進行了詳細分解,以便發現性能瓶頸所在。
為了解timeit模塊,我們將查看一些小實例。假定有3個函數function_a()、 function_b()、function_c(), 3個函數執行同樣的計算,但分別使用不同的演算法。如果將這些函數放於同一個模塊中(或分別導入),就可以使用timeit模塊對其進行運行和比較。下面給出的是模塊最後使用的代碼:
if __name__ == "__main__":
repeats = 1000
for function in ("function_a", "function_b", "function_c"):
t = timeit.Timer("{0}(X, Y)".format(function),"from __main__ import {0}, X, Y".format(function))
sec = t.timeit(repeats) / repeats
print("{function}() {sec:.6f} sec".format(**locals()))
賦予timeit.Timer()構造子的第一個參數是我們想要執行並計時的代碼,其形式是字元串。這里,該字元串是「function_a(X,Y)」;第二個參數是可選的,還是一個待執行的字元串,這一次是在待計時的代碼之前,以便提供一些建立工作。這里,我們從 __main__ (即this)模塊導入了待測試的函數,還有兩個作為輸入數據傳入的變數(X 與Y),這兩個變數在該模塊中是作為全局變數提供的。我們也可以很輕易地像從其他模塊中導入數據一樣來進行導入操作。
調用timeit.Timer對象的timeit()方法時,首先將執行構造子的第二個參數(如果有), 之後執行構造子的第一個參數並對其執行時間進行計時。timeit.Timer.timeit()方法的返回值是以秒計數的時間,類型是float。默認情況下,timeit()方法重復100萬次,並返回所 有這些執行的總秒數,但在這一特定案例中,只需要1000次反復就可以給出有用的結果, 因此對重復計數次數進行了顯式指定。在對每個函數進行計時後,使用重復次數對總數進行除法操作,就得到了平均執行時間,並在控制台中列印出函數名與執行時間。
function_a() 0.001618 sec
function_b() 0.012786 sec
function_c() 0.003248 sec
在這一實例中,function_a()顯然是最快的——至少對於這里使用的輸入數據而言。 在有些情況下一一比如輸入數據不同會對性能產生巨大影響——可能需要使用多組輸入數據對每個函數進行測試,以便覆蓋有代表性的測試用例,並對總執行時間或平均執行時間進行比較。
有時監控自己的代碼進行計時並不是很方便,因此timeit模塊提供了一種在命令行中對代碼執行時間進行計時的途徑。比如,要對MyMole.py模塊中的函數function_a()進行計時,可以在控制台中輸入如下命令:python3 -m timeit -n 1000 -s "from MyMole import function_a, X, Y" "function_a(X, Y)"(與通常所做的一樣,對 Windows 環境,我們必須使用類似於C:Python3lpython.exe這樣的內容來替換python3)。-m選項用於Python 解釋器,使其可以載入指定的模塊(這里是timeit),其他選項則由timeit模塊進行處理。 -n選項指定了循環計數次數,-s選項指定了要建立,最後一個參數是要執行和計時的代碼。命令完成後,會向控制台中列印運行結果,比如:
1000 loops, best of 3: 1.41 msec per loop
之後我們可以輕易地對其他兩個函數進行計時,以便對其進行整體的比較。
cProfile模塊(或者profile模塊,這里統稱為cProfile模塊)也可以用於比較函數 與方法的性能。與只是提供原始計時的timeit模塊不同的是,cProfile模塊精確地展示 了有什麼被調用以及每個調用耗費了多少時間。下面是用於比較與前面一樣的3個函數的代碼:
if __name__ == "__main__":
for function in ("function_a", "function_b", "function_c"):
cProfile.run("for i in ranged 1000): {0}(X, Y)".format(function))
我們必須將重復的次數放置在要傳遞給cProfile.run()函數的代碼內部,但不需要做任何創建,因為模塊函數會使用內省來尋找需要使用的函數與變數。這里沒有使用顯式的print()語句,因為默認情況下,cProfile.run()函數會在控制台中列印其輸出。下面給出的是所有函數的相關結果(有些無關行被省略,格式也進行了稍許調整,以便與頁面適應):
1003 function calls in 1.661 CPU seconds
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.003 0.003 1.661 1.661 :1 ( )
1000 1.658 0.002 1.658 0.002 MyMole.py:21 (function_a)
1 0.000 0.000 1.661 1.661 {built-in method exec}
5132003 function calls in 22.700 CPU seconds
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.487 0.487 22.700 22.700 : 1 ( )
1000 0.011 0.000 22.213 0.022 MyMole.py:28(function_b)
5128000 7.048 0.000 7.048 0.000 MyMole.py:29( )
1000 0.00 50.000 0.005 0.000 {built-in method bisectjeft}
1 0.000 0.000 22.700 22.700 {built-in method exec}
1000 0.001 0.000 0.001 0.000 {built-in method len}
1000 15.149 0.015 22.196 0.022 {built-in method sorted}
5129003 function calls in 12.987 CPU seconds
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.205 0.205 12.987 12.987 :l ( )
1000 6.472 0.006 12.782 0.013 MyMole.py:36(function_c)
5128000 6.311 0.000 6.311 0.000 MyMole.py:37( )
1 0.000 0.000 12.987 12.987 {built-in method exec}
ncalls ("調用的次數")列列出了對指定函數(在filename:lineno(function)中列出) 的調用次數。回想一下我們重復了 1000次調用,因此必須將這個次數記住。tottime (「總的時間」)列列出了某個函數中耗費的總時間,但是排除了函數調用的其他函數內部花費的時間。第一個percall列列出了對函數的每次調用的平均時間(tottime // ncalls)。 cumtime ("累積時間")列出了在函數中耗費的時間,並且包含了函數調用的其他函數內部花費的時間。第二個percall列列出了對函數的每次調用的平均時間,包括其調用的函數耗費的時間。
這種輸出信息要比timeit模塊的原始計時信息富有啟發意義的多。我們立即可以發現,function_b()與function_c()使用了被調用5000次以上的生成器,使得它們的速度至少要比function_a()慢10倍以上。並且,function_b()調用了更多通常意義上的函數,包括調用內置的sorted()函數,這使得其幾乎比function_c()還要慢兩倍。當然,timeit() 模塊提供了足夠的信息來查看計時上存在的這些差別,但cProfile模塊允許我們了解為什麼會存在這些差別。正如timeit模塊允許對代碼進行計時而又不需要對其監控一樣,cProfile模塊也可以做到這一點。然而,從命令行使用cProfile模塊時,我們不能精確地指定要執行的 是什麼——而只是執行給定的程序或模塊,並報告所有這些的計時結果。需要使用的 命令行是python3 -m cProfile programOrMole.py,產生的輸出信息與前面看到的一 樣,下面給出的是輸出信息樣例,格式上進行了一些調整,並忽略了大多數行:
10272458 function calls (10272457 primitive calls) in 37.718 CPU secs
ncalls tottime percall cumtime percall filename:lineno(function)
10.000 0.000 37.718 37.718 :1 ( )
10.719 0.719 37.717 37.717 :12( )
1000 1.569 0.002 1.569 0.002 :20(function_a)
1000 0.011 0.000 22.560 0.023 :27(function_b)
5128000 7.078 0.000 7.078 0.000 :28( )
1000 6.510 0.007 12.825 0.013 :35(function_c)
5128000 6.316 0.000 6.316 0.000 :36( )
在cProfile術語學中,原始調用指的就是非遞歸的函數調用。
以這種方式使用cProfile模塊對於識別值得進一步研究的區域是有用的。比如,這里 我們可以清晰地看到function_b()需要耗費更長的時間,但是我們怎樣獲取進一步的詳細資料?我們可以使用cProfile.run("function_b()")來替換對function_b()的調用。或者可以保存完全的profile數據並使用pstats模塊對其進行分析。要保存profile,就必須對命令行進行稍許修改:python3 -m cProfile -o profileDataFile programOrMole.py。 之後可以對 profile 數據進行分析,比如啟動IDLE,導入pstats模塊,賦予其已保存的profileDataFile,或者也可以在控制台中互動式地使用pstats。
下面給出的是一個非常短的控制台會話實例,為使其適合頁面展示,進行了適當調整,我們自己的輸入則以粗體展示:
$ python3 -m cProfile -o profile.dat MyMole.py
$ python3 -m pstats
Welcome to the profile statistics browser.
% read profile.dat
profile.dat% callers function_b
Random listing order was used
List reced from 44 to 1 e to restriction
Function was called by...
ncalls tottime cumtime
:27(function_b) <- 1000 0.011 22.251 :12( )
profile.dat% callees function_b
Random listing order was used
List reced from 44 to 1 e to restriction
Function called...
ncalls tottime cumtime
:27(function_b)->
1000 0.005 0.005 built-in method bisectJeft
1000 0.001 0.001 built-in method len
1000 1 5.297 22.234 built-in method sorted
profile.dat% quit
輸入help可以獲取命令列表,help後面跟隨命令名可以獲取該命令的更多信息。比如, help stats將列出可以賦予stats命令的參數。還有其他一些可用的工具,可以提供profile數據的圖形化展示形式,比如 RunSnakeRun (www.vrplumber.com/prograinming/runsnakerun), 該工具需要依賴於wxPython GUI庫。
使用timeit與cProfile模塊,我們可以識別出我們自己代碼中哪些區域會耗費超過預期的時間;使用cProfile模塊,還可以准確算岀時間消耗在哪裡。
以上內容部分摘自視頻課程 05後端編程Python-19調試、測試和性能調優(下) ,更多實操示例請參照視頻講解。跟著張員外講編程,學習更輕松,不花錢還能學習真本領。
『捌』 python有什麼進階的書
1、《Python核心編程》第二版以及第三版
這是一本有關於Python開發的權威指南,無論你是想在哪個Python方向發展,相信這本書都會成為你好助手。
2、《流暢的Python》
該書更像是一本工具書,你可以從本書的任意一章開始閱讀,如果按照作者寫作時的構思來說,本書一共分為六個獨立的部分,讀者最好按照順序來閱讀,收獲會更多。
3、《Effective+Python》編寫高質量Python代碼的59個有效方法
這本書無論是對初學者還是熟練者都有較大意義,對於初學者,該書展示了大體輪廓,使我們能夠知道Python的強項和弱項,在知道這些特性後,開發者就可以結合自己的興趣與需求,有選擇、有順序的學習。
對於熟練者而言,可以把書中的心得與自己的經驗相比對,看看自己還有哪些區域尚未深入研究,並思考一下書中的方案與自己常用的方案各有什麼優點和缺點。總之,這本書既可以像字典一樣查閱,額可以像普通圖書那樣閱讀。
4、《精通Python設計模式》
這本書分為三部分,共16個章節,介紹一些常用的設計模式,第一部分介紹處理對象創建的設計模式,包括工廠模式、建造者模式、原型模式;第二部分介紹處理一個系統中不同實體,如類、對象等之間關系的設計模式;第三部分介紹處理系統實體之間通信的設計模式,包括責任鏈模式、觀察者模式等。
5、《Python高級編程》
本書通過大量的實例,介紹了Python語言的最佳實踐和敏捷開發的方法,並涉及整個軟體生命周期的高級主題,諸如持續集成、版本控制系統、包的發行和分發、開發模式、文檔編寫等。
本書針對具備一定的Python基礎並希望通過在項目中應用最佳實踐和新的開發技術來提升自己的Python開發人員。
6、《Python性能分析與優化》
這本書首先會介紹什麼是性能分析,性能分析如何在項目開發周期中發揮作用,以及通過在項目中的進行性能分析實踐能夠取得的效果。緊接著將介紹分析性能所需的核心工具(性能分析器和可視化性能分析器)。然後會討論一系列性能優化技術,最後一章會介紹一個具有實際意義的優化示例。
7、《Python開發實戰》第1、2版
本書來自真正的開發現場,是眾多極客們在真實項目中的經驗總結。
作者從Python的環境搭建開始講起,介紹了以web應用開發方法、任務管理,以及評審、測試及高效部署、伺服器調試等內容,盡可能網羅了Python開發流程中的方方面面,在這里Python僅僅是一個載體,很多知識點在非Python下也適用,這也是這本書最大的特點。
8、《Python高手之路》第3版
首先,這肯定不是一本入門書,書中沒有Python關鍵字和for循環等,它完全從實戰的角度出發,對構建一個完整的Python應用所需掌握的知識進行系統而完整的介紹。更重要的是本書的作者是開源項目OpenStack的PTL之一,所以這本書結合了Python在OpenStack中的應用進行講解,非常具有實戰指導意義。
眾多python培訓視頻,盡在python學習網,歡迎在線學習!
『玖』 如何提高python的運行效率
竅門一:關鍵代碼使用外部功能包
Python簡化了許多編程任務,但是對於一些時間敏感的任務,它的表現經常不盡人意。使用C/C++或機器語言的外部功能包處理時間敏感任務,可以有效提高應用的運行效率。這些功能包往往依附於特定的平台,因此你要根據自己所用的平台選擇合適的功能包。簡而言之,這個竅門要你犧牲應用的可移植性以換取只有通過對底層主機的直接編程才能獲得的運行效率。以下是一些你可以選擇用來提升效率的功能包:
Cython
Pylnlne
PyPy
Pyrex
這些功能包的用處各有不同。比如說,使用C語言的數據類型,可以使涉及內存操作的任務更高效或者更直觀。Pyrex就能幫助Python延展出這樣的功能。Pylnline能使你在Python應用中直接使用C代碼。內聯代碼是獨立編譯的,但是它把所有編譯文件都保存在某處,並能充分利用C語言提供的高效率。
竅門二:在排序時使用鍵
Python含有許多古老的排序規則,這些規則在你創建定製的排序方法時會佔用很多時間,而這些排序方法運行時也會拖延程序實際的運行速度。最佳的排序方法其實是盡可能多地使用鍵和內置的sort()方法。譬如,拿下面的代碼來說:
import operator
somelist = [(1, 5,]
在每段例子里,list都是根據你選擇的用作關鍵參數的索引進行排序的。這個方法不僅對數值類型有效,還同樣適用於字元串類型。
竅門三:針對循環的優化
每一種編程語言都強調最優化的循環方案。當使用Python時,你可以藉助豐富的技巧讓循環程序跑得更快。然而,開發者們經常遺忘的一個技巧是:盡量避免在循環中訪問變數的屬性。譬如,拿下面的代碼來說:
lowerlist = ['this', 'is', 'lowercase']
upper = str.upper
upperlist = []
append = upperlist.append
for word in lowerlist:
append(upper(word))
print(upperlist)
#Output = ['THIS', 'IS', 'LOWERCASE']
每次你調用str.upper, Python都會計算這個式子的值。然而,如果你把這個求值賦值給一個變數,那麼求值的結果就能提前知道,Python程序就能運行得更快。因此,關鍵就是盡可能減小Python在循環中的工作量。因為Python解釋執行的特性,在上面的例子中會大大減慢它的速度。
(注意:優化循環的方法還有很多,這只是其中之一。比如,很多程序員會認為,列表推導式是提高循環速度的最佳方法。關鍵在於,優化循環方案是提高應用程序運行速度的上佳選擇。)
竅門四:使用較新的Python版本
如果你在網上搜索Python,你會發現數不盡的信息都是關於如何升級Python版本。通常,每個版本的Python都會包含優化內容,使其運行速度優於之前的版本。但是,限制因素在於,你最喜歡的函數庫有沒有同步更新支持新的Python版本。與其爭論函數庫是否應該更新,關鍵在於新的Python版本是否足夠高效來支持這一更新。
你要保證自己的代碼在新版本里還能運行。你需要使用新的函數庫才能體驗新的Python版本,然後你需要在做出關鍵性的改動時檢查自己的應用。只有當你完成必要的修正之後,你才能體會新版本的不同。
然而,如果你只是確保自己的應用在新版本中可以運行,你很可能會錯過新版本提供的新特性。一旦你決定更新,請分析你的應用在新版本下的表現,並檢查可能出問題的部分,然後優先針對這些部分應用新版本的特性。只有這樣,用戶才能在更新之初就覺察到應用性能的改觀。
竅門五:嘗試多種編碼方法
每次創建應用時都使用同一種編碼方法幾乎無一例外會導致應用的運行效率不盡人意。可以在程序分析時嘗試一些試驗性的辦法。譬如說,在處理字典中的數據項時,你既可以使用安全的方法,先確保數據項已經存在再進行更新,也可以直接對數據項進行更新,把不存在的數據項作為特例分開處理。請看下面第一段代碼:
n = 16
myDict = {}
for i in range(0, n):
char = 'abcd'[i%4]
if char not in myDict:
myDict[char] = 0
myDict[char] += 1
print(myDict)
當一開始myDict為空時,這段代碼會跑得比較快。然而,通常情況下,myDict填滿了數據,至少填有大部分數據,這時換另一種方法會更有效率。
n = 16
myDict = {}
for i in range(0, n):
char = 'abcd'[i%4]
try:
myDict[char] += 1
except KeyError:
myDict[char] = 1
print(myDict)
在兩種方法中輸出結果都是一樣的。區別在於輸出是如何獲得的。跳出常規的思維模式,創建新的編程技巧能使你的應用更有效率。
竅門六:交叉編譯你的應用
開發者有時會忘記計算機其實並不理解用來創建現代應用程序的編程語言。計算機理解的是機器語言。為了運行你的應用,你藉助一個應用將你所編的人類可讀的代碼轉換成機器可讀的代碼。有時,你用一種諸如Python這樣的語言編寫應用,再以C++這樣的語言運行你的應用,這在運行的角度來說,是可行的。關鍵在於,你想你的應用完成什麼事情,而你的主機系統能提供什麼樣的資源。
Nuitka是一款有趣的交叉編譯器,能將你的Python代碼轉化成C++代碼。這樣,你就可以在native模式下執行自己的應用,而無需依賴於解釋器程序。你會發現自己的應用運行效率有了較大的提高,但是這會因平台和任務的差異而有所不同。
(注意:Nuitka現在還處在測試階段,所以在實際應用中請多加註意。實際上,當下最好還是把它用於實驗。此外,關於交叉編譯是否為提高運行效率的最佳方法還存在討論的空間。開發者已經使用交叉編譯多年,用來提高應用的速度。記住,每一種解決辦法都有利有弊,在把它用於生產環境之前請仔細權衡。)
在使用交叉編譯器時,記得確保它支持你所用的Python版本。Nuitka支持Python2.6, 2.7, 3.2和3.3。為了讓解決方案生效,你需要一個Python解釋器和一個C++編譯器。Nuitka支持許多C++編譯器,其中包括Microsoft Visual Studio,MinGW 和 Clang/LLVM。
交叉編譯可能造成一些嚴重問題。比如,在使用Nuitka時,你會發現即便是一個小程序也會消耗巨大的驅動空間。因為Nuitka藉助一系列的動態鏈接庫(DDLs)來執行Python的功能。因此,如果你用的是一個資源很有限的系統,這種方法或許不太可行。
『拾』 python寫演算法不好
基於以下三個原因,我們選擇Python作為實現機器學習演算法的編程語言:(1) python的語法清晰;(2) 易於操作純文本文件;(3) 使用廣泛,存在大量的開發文檔。
可執行偽代碼
Python具有清晰的語法結構,大家也把它稱作可執行偽代碼(executable pseudo-code)。默認安裝的Python開發環境已經附帶了很多高級數據類型,如列表、元組、字典、集合、隊列等,無需進一步編程就可以使用這些數據類型的操作。使用這些數據類型使得實現抽象的數學概念非常簡單。此外,讀者還可以使用自己熟悉的編程風格,如面向對象編程、面向過程編程、或者函數式編程。
Python語言處理和操作文本文件非常簡單,非常易於處理非數值型數據。Python語言提供了豐富的正則表達式函數以及很多訪問Web頁面的函數庫,使得從HTML中提取數據變得非常簡單直觀。
Python比較流行
Python語言使用廣泛,代碼範例也很多,便於讀者快速學習和掌握。此外,在開發實際應用程序時,也可以利用豐富的模塊庫縮短開發周期。
在科學和金融領域,Python語言得到了廣泛應用。SciPy和NumPy等許多科學函數庫都實現了向量和矩陣操作,這些函數庫增加了代碼的可讀性,學過線性代數的人都可以看懂代碼的實際功能。另外,科學函數庫SciPy和NumPy使用底層語言(C和Fortran)編寫,提高了相關應用程序的計算性能。本書將大量使用Python的NumPy。
Python的科學工具可以與繪圖工具Matplotlib協同工作。Matplotlib可以繪制2D、3D圖形,也可以處理科學研究中經常使用到的圖形,所以本書也將大量使用Matplotlib。
Python開發環境還提供了互動式shell環境,允許用戶開發程序時查看和檢測程序內容。
Python開發環境將來還會集成Pylab模塊,它將NumPy、SciPy和Matplotlib合並為一個開發環境。在本書寫作時,Pylab還沒有並入Python環境,但是不遠的將來我們肯定可以在Python開發環境找到它。
Python語言的特色
諸如MATLAB和Mathematica等高級程序語言也允許用戶執行矩陣操作,MATLAB甚至還有許多內嵌的特徵可以輕松地構造機器學習應用,而且MATLAB的運算速度也很快。然而MATLAB的不足之處是軟體費用太高,單個軟體授權就要花費數千美元。雖然也有適合MATLAB的第三方插件,但是沒有一個有影響力的大型開源項目。
Java和C等強類型程序設計語言也有矩陣數學庫,然而對於這些程序設計語言來說,最大的問題是即使完成簡單的操作也要編寫大量的代碼。程序員首先需要定義變數的類型,對於Java來說,每次封裝屬性時還需要實現getter和setter方法。另外還要記著實現子類,即使並不想使用子類,也必須實現子類方法。為了完成一個簡單的工作,我們必須花費大量時間編寫了很多無用冗長的代碼。Python語言則與Java和C完全不同,它清晰簡練,而且易於理解,即使不是編程人員也能夠理解程序的含義,而Java和C對於非編程人員則像天書一樣難於理解。
所有人在小學二年級已經學會了寫作,然而大多數人必須從事其他更重要的工作。
——鮑比·奈特
也許某一天,我們可以在這句話中將「寫作」替代為「編寫代碼」,雖然有些人對於編寫代碼很感興趣,但是對於大多數人來說,編程僅是完成其他任務的工具而已。Python語言是高級編程語言,我們可以花費更多的時間處理數據的內在含義,而無須花費太多精力解決計算機如何得到數據結果。Python語言使得我們很容易表達自己的目的。
Python語言的缺點
Python語言唯一的不足是性能問題。Python程序運行的效率不如Java或者C代碼高,但是我們可以使用Python調用C編譯的代碼。這樣,我們就可以同時利用C和Python的優點,逐步地開發機器學習應用程序。我們可以首先使用Python編寫實驗程序,如果進一步想要在產品中實現機器學習,轉換成C代碼也不困難。如果程序是按照模塊化原則組織的,我們可以先構造可運行的Python程序,然後再逐步使用C代碼替換核心代碼以改進程序的性能。C++ Boost庫就適合完成這個任務,其他類似於Cython和PyPy的工具也可以編寫強類型的Python代碼,改進一般Python程序的性能。
如果程序的演算法或者思想有缺陷,則無論程序的性能如何,都無法得到正確的結果。如果解決問題的思想存在問題,那麼單純通過提高程序的運行效率,擴展用戶規模都無法解決這個核心問題。從這個角度來看,Python快速實現系統的優勢就更加明顯了,我們可以快速地檢驗演算法或者思想是否正確,如果需要,再進一步優化代碼。