從編寫過程式腳本轉換到面向對象的編程通常是非常困難的。本文探索如何重用來自 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 伺服器的對外服務目錄中。
<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% / |
下面讓我們看一下如何在 Bash 函數中完成同樣的事情。在 Bash 中,該問題要容易解決得多,因為您實際上是在處理系統調用。在此示例中,您甚至不需要使用數組或正則表達式庫,因為使用到 grep 的管道容易多了。不過,在 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 來創建這同一個腳本的過程式版本。
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
過程式編程通常是初學的開發人員的最自然編程風格,並且對於許多問題來說也是高度有效的。另一方面,對於創建抽象從而創建可重用的代碼來說,面向對象的編程可能是非常有用的方法。然而,當項目達到某種程度的複雜性之後,過程代碼通常會暴露出其根本缺陷。下面讓我們直接進入上一個示例的面向對象版本,並看看這樣有何變化。
#!/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 函數現在是該類中的一個方法。
使用這種新的樣式,您無需更改原始代碼即可容易地重用和自定義各個部分,而使用過程代碼時則通常必須更改原始代碼。面向對象的設計的一個更加功能強大、通常也被過度使用的方面是繼承。繼承允許您在新的類中重用和自定義現有的代碼。讓我們在下一個示例中看看繼承可能像什麼樣子。
#!/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 |
允許您重用其他類中的屬性的 Python 魔法是這個“MyDiskMonitor(DiskMonitor)”語句。您只需在定義新類的名稱時,將先前的類的名稱放在括弧內。一旦完成此步驟,您立即可以訪問其他類屬性來做自己希望的事情。但是樂趣不僅於此。通過添加另一個通過電子郵件來發送標記消息的方法,也許是將其命名為 disk_alert(self),這樣就可以進一步自定義新類。這是面向對象的設計的美妙之處;它允許有經驗的開發人員不斷重用已編寫的代碼,從而節省大量的時間。
遺憾的是,面向對象的編程也有其不利的一面。所有這些抽象都是以複雜性為代價的,如果抽象過度,可能會徹底地弄巧成拙。由於 Python 支持多重繼承,抽象可以達到相當有害的複雜程度。您是否能夠想象只是為了編寫一個方法也要查看多個文件的情況?無論相信與否,這種情況的確會發生,並且代表了面向對象編程的不幸現實。
面向對象的編程的替代方案是函數式編程,並且 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次圍觀