歡迎您光臨本站 註冊首頁

從腳本編寫到面向對象的 Python 編程

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  
從編寫過程式腳本轉換到面向對象的編程通常是非常困難的。本文探索如何重用來自 PHP、Bash 或 Python 腳本的程序,轉換到 Python 中的面向對象的編程。本文還將簡略地談到函數式編程的適當使用。

引言

Python 在近年來的受歡迎程度劇增,部分原因在於該語言非常靈活,同時功能非常強大。Python 可用於系統管理、Web 開發、GUI 編程、科學計算等等。本文的主要目標是向習慣於使用 Bash、PHP 或其它某種語言編寫腳本過程代碼的人介紹面向對象的 Python 開發,並幫助他們轉換到面向對象的 Python 開發。Python 的這種日益流行性意味著,對於目前使用其他編程語言的開發人員,除了使用他們最喜歡的語言之外,他們還可以採用 Python 來完成某些項目。

過程式編程當然有其用武之地,並且可能是解決某個問題的高度有效的方法。在非常基本的層次上,過程式編程可定義為指令的列表,Bash 和 PHP 通常就是以這樣的方式編寫的。然而由於 Python 的流行,對於作為 Web 開發人員或系統管理員的 PHP 和 Bash 腳本編寫人員,他們正陷入必須同時學習面向對象的編程和 Python 的境地。

面向對象這個概念很難一次性地掌握,因此本文採用過程式 Bash 和 PHP 腳本,並首先將它們轉換為過程式 Python。作為最後一步,它們將轉換為面向對象的 Python 這個終結目標。本文在結束時將簡略討論一下面向對象的 Python 的一些優點,然後在最後討論一些可能更適合採用過程或函數式編程的一些不利場景。到本文結束時,Bash 或 PHP 程序員應該能夠毫無畏懼地一頭扎進面向對象的 Python 項目。

如果您以前沒有聽說過函數式編程,我強烈建議您閱讀參考資料部分中的一些有關函數式編程的文章。不過簡而言之,可以將函數式編程描述為“分發函數”。通常,與面向對象的編程相比,函數式編程是表達某個想法的更簡潔和更清楚的方法。

採用 PHP 和 Bash 編寫磁碟監視函數

雖然 PHP 主要是為了在瀏覽器中運行,但是也可以通過 exec 函數執行系統調用。採用 PHP 編寫的第一個示例將捕獲 Shell 命令“df –h”的輸出,將輸出放在一個數組中,然後根據一個正則表達式檢查輸出的每一行。如果該行與正則表達式匹配,則列印該行。如果您希望從主目錄運行此示例,只需將此腳本命名為 index.php,並將其放在 Apache/mod_php 伺服器的對外服務目錄中。


PHP 磁碟監視示例
                  <html>  <body>  <?php    //Analyzes disk usage  //Takes regex pattern and message  function disk_space( $pattern="/2[0-9]%/", $message="CAPACITY WARNING:" )    {      exec(escapeshellcmd("df -h"),$output_lines,$return_value);      foreach ($output_lines as $output) {          if (preg_match( $pattern, $output ))              echo "<b>$message</b> $output <br />";        }  }    disk_space()    ?>  </body>  </html>                  

如果您在瀏覽器中運行此網頁,將會獲得以下結果:

        CAPACITY WARNING: /dev/sda1 3.8G 694M 2.9G 20% /         

查看該代碼,可以看到正則表達式模式被設置為匹配某個包含 20-29% 的行。可以容易地修改此模式以適應其他標誌,例如 90-99%,因為 20% 是非常低的磁碟容量。

下面讓我們看一下如何在 Bash 函數中完成同樣的事情。在 Bash 中,該問題要容易解決得多,因為您實際上是在處理系統調用。在此示例中,您甚至不需要使用數組或正則表達式庫,因為使用到 grep 的管道容易多了。不過,在 Bash 中設置函數的預設參數始終有點麻煩。


Bash 磁碟監視示例
                  #!/usr/bin/env bash    #function flags disk usage takes pattern and message optionally  function disk_space ()  {      #checks for pattern parameter      if [ "$1" != "" ]; then          pattern=$1      else          pattern="2[0-9]%"      fi        #checks for message parameter      if [ "$2" != "" ]; then          message=$2      else          message="CAPACITY WARNING:"      fi        #looks at output for pattern to flag      output_lines=`df -h | grep $pattern`      if [ "$output_lines" != "" ]; then          echo $message $output_lines      fi  }    #example of optional parameters usage  #disk_space 9[0-9]% ALERT:    disk_space        

當您運行此腳本時,將會獲得同樣的輸出,因此可以跳過輸出的顯示。您能夠從該腳本的 PHP 版本和 Bash 版本中找到的相關性在於,此過程式代碼事實上像一組指令一樣運行。似乎計算機就像是一個小孩,而您告訴該小孩如何做某件事情,例如第一次系鞋帶。在您開始在 Python 中考慮“面向對象範式”之前,讓我們首先看一下如何採用 Python 來創建這同一個腳本的過程式版本。


Python 磁碟監視示例
                  from subprocess import Popen, PIPE  import re    def disk_space(pattern="2[0-9]%", message="CAPACITY WARNING:"):            #takes shell command output          ps = Popen("df -h", shell=True,stdout=PIPE, stderr=PIPE)          output_lines = ps.stdout.readlines()          for line in output_lines():              line = line.strip()              if re.search(pattern,line):                  print "%s %s" % (message,line)    disk_space()        

瀏覽一下我們的代碼的過程式 Python 版本,發現它與 Bash 和 PHP 版本非常相似。對於 Python,子過程模塊處理對 Shell 命令的系統調用,並將輸出發在一個列表(在 Bash 和 PHP 中稱為數組)中。與 PHP 版本非常相似,然後我對命令的標準輸出行列表中的項進行了迭代遍歷。我尋找構成所尋找模式的正則表達式,然後使用注入的特殊消息來列印該磁碟報告行。這是如何解決自頂向下的腳本問題的經典示例,但是在下一個部分中,您將完全改變這種方法,並從對象的角度考慮問題。

從過程到面向對象的 Python

過程式編程通常是初學的開發人員的最自然編程風格,並且對於許多問題來說也是高度有效的。另一方面,對於創建抽象從而創建可重用的代碼來說,面向對象的編程可能是非常有用的方法。然而,當項目達到某種程度的複雜性之後,過程代碼通常會暴露出其根本缺陷。下面讓我們直接進入上一個示例的面向對象版本,並看看這樣有何變化。


面向對象的 Python 磁碟監視腳本
                  #!/usr/bin/env python    from subprocess import Popen, PIPE  import re    class DiskMonitor():      """Disk Monitoring Class"""      def __init__(self,                  pattern="2[0-9]%",                  message="CAPACITY WARNING",                  cmd = "df -h"):          self.pattern = pattern          self.message = message          self.cmd = cmd        def disk_space(self):          """Disk space capacity flag method"""          ps = Popen(self.cmd, shell=True,stdout=PIPE,stderr=PIPE)          output_lines = ps.stdout.readlines()          for line in output_lines:              line = line.strip()              if re.search(self.pattern,line):                  print "%s %s" % (self.message,line)    if __name__ == "__main__":      d = DiskMonitor()      d.disk_space()          

查看該代碼的面向對象版本,可以看到代碼變得更加抽象。有時,太多的抽象會導致設計問題,但是在此例中,它允許您將問題分離為更多可重用的部分。DiskMonitor 類具有 __init__ method,您可以在其中定義新的參數,並且 disk_space 函數現在是該類中的一個方法。

使用這種新的樣式,您無需更改原始代碼即可容易地重用和自定義各個部分,而使用過程代碼時則通常必須更改原始代碼。面向對象的設計的一個更加功能強大、通常也被過度使用的方面是繼承。繼承允許您在新的類中重用和自定義現有的代碼。讓我們在下一個示例中看看繼承可能像什麼樣子。


使用繼承的面向對象 Python 磁碟監視腳本
                  #!/usr/bin/env python    from subprocess import Popen, PIPE  import re    class DiskMonitor():      """Disk Monitoring Class"""      def __init__(self,                  pattern="2[0-9]%",                  message="CAPACITY WARNING",                  cmd = "df -h"):          self.pattern = pattern          self.message = message          self.cmd = cmd        def disk_space(self):          """Disk space capacity flag method"""          ps = Popen(self.cmd, shell=True,stdout=PIPE,stderr=PIPE)          output_lines = ps.stdout.readlines()          for line in output_lines:              line = line.strip()              if re.search(self.pattern,line):                  print "%s %s" % (self.message,line)    class MyDiskMonitor(DiskMonitor):      """Customized Disk Monitoring Class"""        def disk_space(self):          ps = Popen(self.cmd, shell=True,stdout=PIPE,stderr=PIPE)          print "RAW DISK REPORT:"          print ps.stdout.read()    if __name__ == "__main__":      d = MyDiskMonitor()      d.disk_space()                             

如果運行這個使用繼承的腳本版本,您將獲得以下輸出:

RAW DISK REPORT:  Filesystem            Size  Used Avail Use% Mounted on  /dev/sda1             3.8G  694M  2.9G  20% /  varrun                252M   48K  252M   1% /var/run  varlock               252M     0  252M   0% /var/lock  udev                  252M   52K  252M   1% /dev  devshm                252M     0  252M   0% /dev/shm        

此輸出與前面帶標記的版本區別非常大,因為它只是使用頂部注入的 print 語句來列印的未經篩選的 df –h 命令結果。通過重寫 MyDiskMonitor 類中的方法,您能夠完全改變 disk_space 方法的意圖。

允許您重用其他類中的屬性的 Python 魔法是這個“MyDiskMonitor(DiskMonitor)”語句。您只需在定義新類的名稱時,將先前的類的名稱放在括弧內。一旦完成此步驟,您立即可以訪問其他類屬性來做自己希望的事情。但是樂趣不僅於此。通過添加另一個通過電子郵件來發送標記消息的方法,也許是將其命名為 disk_alert(self),這樣就可以進一步自定義新類。這是面向對象的設計的美妙之處;它允許有經驗的開發人員不斷重用已編寫的代碼,從而節省大量的時間。

遺憾的是,面向對象的編程也有其不利的一面。所有這些抽象都是以複雜性為代價的,如果抽象過度,可能會徹底地弄巧成拙。由於 Python 支持多重繼承,抽象可以達到相當有害的複雜程度。您是否能夠想象只是為了編寫一個方法也要查看多個文件的情況?無論相信與否,這種情況的確會發生,並且代表了面向對象編程的不幸現實。

面向對象的編程的替代方案是函數式編程,並且 Python 提供了用於進行函數式以及面向對象和過程式編程的資源。在最後一個示例中,我們將研究如何以函數式的方式編寫現已變得非常無聊的磁碟監視代碼。

存在一種解決濫用繼承問題的方法:考慮是否可以將問題重構為“A 包含 B”(a.b) 而不是“A 是 B 的子類”(A(B))。如果是,則“包含”方法幾乎始終會更好。

函數式的 Python 磁碟監視腳本
                  from subprocess import Popen, PIPE  import re    def disk_space(pattern="2[0-9]%", message="CAPACITY WARNING:"):      #Generator Pipeline To Search For Critical Items      ps = Popen("df -h", shell=True,stdout=PIPE, stderr=PIPE)      outline = (line.split() for line in ps.stdout)      flag = (" ".join(row) for row in outline if re.search(pattern, row[-2]))      for line in flag:          print "%s %s" % (message,line)    disk_space()  

查看這最後一個示例,它與您從本文中看到的所有其他代碼的區別都非常大。如果您逐行瀏覽該代碼,可以首先從 “ps”變數中以前未見過的內容開始。接下來的兩行代碼使用生成器表達式來處理文件對象 ps.stdout,分析該文件並在其中搜索您正在查找的行。如果您將這些代碼行剪切並粘貼到互動式的 Python Shell 中,如果列印的話,您將看到概要和標誌都是生成器對象。生成器對象附帶有下一個方法,因而允許您通過“管道”將操作連在一起。

概要行從一行中去除新行字元,並往下將該行傳遞給下一個生成器表達式,後者一次一個地在每行中搜索某個正則表達式匹配項,然後將輸出傳遞給標記。此類緊湊的工作流可以替代面向對象的編程樣式,並且相當有趣。然而,這種樣式也有缺點,因為代碼的簡潔性會導致難於調試的錯誤,除非獨立地執行每一行代碼。函數式編程還很傷腦筋,因為它讓您通過將解決方案鏈接在一起來考慮解決問題。無論是從過程式還是從面向對象樣式的角度看,這都是相當不同的。

總結

本文有點試驗性質,因為它從 Bash 和 PHP 談到了過程、面向對象,並在最後談到了使用相同基本代碼的函數式 Python。但願本文說明了 Python 是一種非常靈活和功能強大的語言,其他編程語言的開發人員也可以學習欣賞。隨著 Python 的越來越流行,其他開發人員除了首選語言之外,學習 Python 也將變得更加重要。

Python 最近的兩個最大的發展領域是 Web 開發和系統管理。就 Web 開發而言,PHP 開發人員可能很快就必須做出每周的選擇,即哪個項目採用 Python 更有意義,以及哪個項目採用 PHP 更有意義。對於系統管理員、Bash 和 Perl 腳本程序員,他們經常被要求採用 Python 完成某些項目。部分是因為這是沒有選擇的,部分是因為許多供應商正在為他們的產品提供 Python API。在您的工具箱中準備一點 Python 決不會傷害任何人。(責任編輯:A6)



[火星人 ] 從腳本編寫到面向對象的 Python 編程已經有665次圍觀

http://coctec.com/docs/linux/show-post-69136.html