解決問題
我們已經探索了 Python 語言中的許多部分,現在我們將通過設計並編寫一款程序來了解如何把這些部分組合到一起。這些程序一定是能做到一些有用的事情。這節的Python教程就是教大家方法去學習如何靠你自己來編寫一份 Python 腳本。
問題
我們希望解決的問題如下:
我想要一款程序來備份我所有的重要文件。
雖然這是一個簡單的問題,但是其中並沒有足夠的信息有助於讓我們開始規划一份解決方案。我們需要進行一些分析(Analysis)。例如,我們應該如何指定哪些文件是我們需要備份的?它們應該如何進行備份?儲存到哪里?
在正確地分析了這些問題過后,我們便開始設計(Design)我們的程序。我們將列出一份關於我們的程序應如何運轉的清單。在這個案例中,我已經編寫了如下清單來說明我將如何工作。如果由你來設計程序,你可能不會做出同樣的分析,因為每個人都有其自己的行事方式,所以出現不同是完全正常、且正確的。
- 需要備份的文件與目錄應在一份列表中予以指定。
- 備份必須存儲在一個主備份目錄中。
- 備份文件將打包壓縮成 zip 文件。
- zip 壓縮文件的文件名由當前日期與時間構成。
- 我們使用在任何 GNU/Linux 或 Unix 發行版中都會默認提供的標准
zip命令進行打包。在這里你需要了解到只要有命令行界面,你就可以使用任何需要用到的壓縮或歸檔命令。
針對 Windows 用戶的提示
Windows 用戶可以從 GnuWin32 項目頁面 上下載並安裝
zip命令,並將C:\Program Files\GnuWin32\bin添加至你的系統的PATH環境變量中,這一操作過程與我們為使系統識別 Python 命令本身所做的事情相同。
解決方案
由於我們的程序設計方案現在已經相當穩定,我們便可以開始編寫代碼,這個過程我們稱之為實現(Implementation)我們的解決方案。
將下述代碼保存為 backup_ver1.py:
import os import time # 1. 需要備份的文件與目錄將被 # 指定在一個列表中。 # 例如在 Windows 下: # source = ['"C:\\My Documents"', 'C:\\Code'] # 又例如在 Mac OS X 與 Linux 下: source = ['/Users/swa/notes'] # 在這里要注意到我們必須在字符串中使用雙引號 # 用以括起其中包含空格的名稱。 #2. 備份文件必須存儲在一個 #主備份目錄中 #例如在 Windows 下: # target_dir = 'E:\\Backup' # 又例如在 Mac OS X 和 Linux 下: target_dir = '/Users/swa/backup' # 要記得將這里的目錄地址修改至你將使用的路徑 # 3. 備份文件將打包壓縮成 zip 文件。 # 4. zip 壓縮文件的文件名由當前日期與時間構成。 target = target_dir + os.sep + \ time.strftime('%Y%m%d%H%M%S') + '.zip' # 如果目標目錄還不存在,則進行創建 if not os.path.exists(target_dir): os.mkdir(target_dir) # 創建目錄 # 5. 我們使用 zip 命令將文件打包成 zip 格式 zip_command = 'zip -r {0} {1}'.format(target, ' '.join(source)) # 運行備份 print('Zip command is:') print(zip_command) print('Running:') if os.system(zip_command) == 0: print('Successful backup to', target) else: print('Backup FAILED') 輸出: $ python backup_ver1.py Zip command is: zip -r /Users/swa/backup/20140328084844.zip /Users/swa/notes Running: adding: Users/swa/notes/ (stored 0%) adding: Users/swa/notes/blah1.txt (stored 0%) adding: Users/swa/notes/blah2.txt (stored 0%) adding: Users/swa/notes/blah3.txt (stored 0%) Successful backup to /Users/swa/backup/20140328084844.zip
現在,我們正處於測試(Testing)階段,在這一階段我們測試我們的程序是否能正常工作。如果其行為不符合我們的預期,那么我們需要對我們的程序進行 Debug 工作,也就是說,移除程序中的 Bug(錯誤)。
如果上面的程序不能夠正常工作,復制打印在 Zip command is 后面的命令,將其粘貼至 shell(在 GNU/Linux 與 Mac OS X 環境中)或 cmd(對於 Windows 環境),看看存在什么錯誤並嘗試將其修復。同時你還需要檢查 zip 命令手冊來看看是不是哪里存在錯誤。如果這條命令成功運行,那么可能是錯誤可能存在在 Python 程序本身之中,因此你需要檢查你的程序是否如上面所展示那番。
它是如何工作的
你會注意到我們是如何一步步將我們的設計轉化為代碼的。
我們首先導入 os 與 time 模塊以准備使用它們。然后,我們在 source 列表中指定我們需要備份的文件與目錄。我們需要存儲我們所有備份文件的目標目錄在 target_dir 變量中予以指定。我們將要創建的 zip 歸檔文件的名字由當前日期與時間構成,在這里通過 time.strftime() python函數來創建。文件名將以 .zip 作為擴展名,並存儲在 target_dir 目錄中。
在這里要注意 os.sep 變量的使用方式——它將根據你的操作系統給出相應的分隔符,在 GNU/Linux 與 Unix 中它會是 '/',在 Windows 中它會是 '\\',在 Mac OS 中它會是 ':'。使用 os.sep 而非直接使用這些字符有助於使我們的程序變得可移植,從而可以在上述這些系統中都能正常工作。
time.strftime() 函數會遵循某些格式(Specification),其中一種就如我們在上方程序中所使用的那樣。%Y 將被替換成帶有具體世紀的年份。%m 將會被替換成以 01 至 12 的十進制數所表示的月份。有關這些格式的全部列表可以在 Python 參考手冊中查詢到。
我們使用連接(Concatenates)字符串的加法(+)運算符來創建目標 zip 文件的文件名,也就是說,它將兩個字符串連接到一起並返回一個新的字符串。然后,我們創建了一串字符串 zip_command,其中包括了我們要執行的命令。如果這條命令不能正常工作,你可以把它拷貝到 Shell(GNU/Linux 終端或 DOS 提示符)中進行檢查。
我們使用的 zip 命令會有一些選項與參數需要傳遞。-r 選項用以指定 zip 命令應該遞歸地(Recursively)對目錄進行工作,也就是說它應該包括所有的子文件夾與其中的文件。這兩個選項結合到一起並可以指定一個快捷方式作 -qr。選項后面跟着的是將要創建的 zip 文件的名稱,再往后是需要備份的文件與目錄的列表。我們通過使用已經討論過並已了解該如何運用的的字符串方法 join 來將列表 source轉換成字符串。
隨后,我們終於可以運行這一使用了 os.system 函數的命令,這一函數可以使命令像是從系統中運行的。也就是說,從 shell 中運行的——如果運行成功,它將返回 0,如果運行失敗,將返回一個錯誤代碼。
根據命令運行的結果是成功還是失敗,我們將打印出與之相應的信息來告訴你備份的結果究竟如何。
就是這樣,我們便創建了一份用以備份我們的重要文件的腳本!
針對 Windows 用戶的提示
除了使用雙反斜杠轉義序列,你還可以使用原始字符串。例如使用
'C:\\Documents'或r'C:\Documents'。然而,不要使用'C:\Documents',因為它將被識別為你使用了一個未知的轉義序列\D來結束路徑的輸入。
現在,我們已經擁有了一份可以正常工作的備份腳本,我們可以在任何我們需要備份文件的時候使用它。這被稱作軟件的操作(Operation)或部署(Deployment)階段。
上面所展示的程序能夠正常工作,但是(通常)第一個程序都不會按照你所期望的進行工作。可能是因為你沒有正確地設計程序,或如果你在輸入代碼時出現了錯誤。出現這些情況時,在恰當的時候,你需要回到設計階段,或者你需要對你的程序進行 Debug 工作。
