天天看點

《Python 3程式開發指南(第2版•修訂版)》——1.3 執行個體

本節書摘來自異步社群《python 3程式開發指南(第2版•修訂版)》一書中的第1章,第1.3節,作者[英]mark summerfield,王弘博,孫傳慶 譯,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

在前面的幾節中,我們介紹了足以編寫實際程式的python知識與技術。在這一節中,我們将介紹兩個完整的程式,這些程式隻涉及前面已經講過的知識。一方面是為了展示前面所學的知識可以完成什麼任務,一方面也是為了鞏固前面所學的知識。

在後面的章節中,我們會逐漸學習更多的python知識與庫子產品,以便于編寫出比這裡展示的程式更精确與更強壯的程式——但是首先我們必須先掌握這些基礎知識。

這裡給出的第一個程式非常短小,盡管該程式也有一些微妙之處,包括清單組成的清單等。這個程式的功能是:在指令行中提供一個數值,之後該程式會使用“大”數字向控制台輸出該數值。

在大量使用者共享高速行式列印機的站點上,使用這種技術是很常見的做法,即為每個使用者的列印作業列印一個引導頁,使其包含使用者名與其他有助于區分不同使用者的詳細資料。

我們分3個部分檢視該程式的代碼:import部分;建立清單(其中存放程式要使用的資料)部分;處理部分本身。不過,我們首先看一下運作的效果:

我們沒有展示控制台提示符(或unix使用者的./),而是将這些内容預設為已經存在的。

由于我們必須從指令行中讀入一個參數(也就是要輸出的數值),我們需要通路sys.argv清單,是以我們從導入sys子產品開始。

我們以字元串清單的形式展示每個數值,比如,下面展示的是zreo:

這裡需要注意的一個細節是,zero的字元串清單表示形式跨越了多個行。通常,python語句隻占用一行,但是也可以跨越多行,比如使用圓括号包含的表達式、清單、集合、字典字面值、函數調用參數清單以及多行語句(除最後一行之外,每行的行終結符都使用反斜線進行引導并轉義處理)。上面的這些python語句可以跨越任意多的行,代碼縮排也并不會影響第二行以及後續的行。

用于表示數值的每個清單包含7個字元串,在對同一個數值的表示中,這些字元串是等寬的,而表示不同數值的字元串寬度不同。表示其他數值的清單在形式上與上面給出的zero類似。下面給出的幾個表示主要是出于緊緻性考慮,因而不那麼形象和清晰:

我們還需要的最後一個資料結構是所有數字清單組成的清單:

我們也可以直接建立digits清單,而不必建立額外的變量,例如:

我們更願意使用單獨的變量來分别表示每個數值,一方面是為了便于了解,另一方面是因為使用變量來表示看起來更整潔。

下面一起展示了餘下的代碼部分,以便你在閱讀其後的解釋之前自己可以設想其工作方式。

上面這段代碼整體包含在一個異常處理子產品中,并可以捕獲兩個異常。該段代碼首先取回程式的指令行參數,與所有python清單類似,sys.argv清單的索引項是從0開始的,索引位置為0的資料項是調用的程式名,是以,在一個運作的程式中,該清單總是至少包含一項。如果沒有給定參數,我們會在一個單資料項的清單中嘗試通路第二個資料項,并導緻産生一個indexerror異常。如果發生這種情況,控制流立即轉向相應的異常處理塊,這裡隻是簡單地列印出程式的用法。在try塊結束後,程式繼續執行,但是由于已經沒有更多的代碼,是以程式隻是簡單地退出。

如果沒有發生indexerror異常,那麼digits字元串會存放指令行參數,如果一切正常,就應該是一個數字字元序列。(記住,要素2中講過,辨別符對大小寫敏感,是以,digits與digits是不同的。)每個大數都使用7個字元串表示,為正确地輸出數值,我們必須首先輸出每個數字的頂行,之後是下一行,依此類推,直至輸出所有的7行。我們使用一個while循環,以便逐行疊代。我們也可以采用另一種方法:for row in (0, 1, 2, 3, 4, 5, 6):,後面我們還将看到一種更好的、使用内置的range()函數的方法。

我們使用line字元串來存放所有數字的行字元串,之後根據列進行循環,也就是說,根據指令行參數中每個相繼的字元進行循環。我們使用digits[column]取回每個字元,并将數字轉換為稱為number的整數。如果轉換失敗,就會産生一個valueerror異常,控制流立即轉向相應的異常處理子產品。這種情況下,我們将列印出錯誤消息,并在try塊之後恢複控制。與前面類似,由于沒有其他代碼等待執行,是以程式隻是簡單地退出。

如果轉換成功,我們就使用number作為索引來存取digits清單,并從其中抽取字元串清單digit,之後我們從這一清單中将相應的字元串添加到我們正在建構的行,并添加兩個空格,以便在數字之間添加水準分隔。

内部的while循環每次結束時,我們會列印出剛建構好的行。了解這一程式的關鍵之處在于我們将每個數字的row字元串添加到目前row的行。讀者可以嘗試運作該程式,以便了解其運作方式。在章後練習中,我們将再次講到該程式,以便對其輸出進行稍許改變。

我們頻繁面臨的需求是測試資料的生成。由于不同場景下測試資料變化巨大,是以無法找到一個滿足所有測試資料需求的通用程式。由于編寫與修改python程式都很容易, python經常被用于生成測試資料。在這一小節中,我們将建立一個生成随機整數組成的網格的程式,使用者可以規定需要多少行、多少列,以及整數所在的區域。我們首先從一個運作執行個體開始:

該程式以互動式的方式運作,最開始在輸入行數時,由于輸入的行數有誤,導緻程式列印一條錯誤消息,并要求使用者重新輸入行數。對于maximum,我們隻是簡單地按enter鍵,以便接受預設值。

我們将分别解讀該程式的4個部分:import、函數get_int()的定義(此函數比要素8中展示的類似函數更複雜)、使用者互動以便擷取要使用的值、處理過程本身。

我們需要random子產品,以便通路其中的random.randinit()函數。

這一函數需要3個參數:一個消息字元串、一個最小值、一個預設值。如果使用者隻是簡單地按enter鍵,就有兩種可能性。如果default為none,也就是說沒有給定預設值,那麼控制流将轉到int()行,在該處轉換将失敗(因為無法轉換為整數),并産生一個valueerror異常;如果default非none,就傳回該值。否則,函數将嘗試把使用者輸入的文本轉換為整數,如果轉換成功,接下來将檢查該整數是否至少等于指定的minimum。

是以,該函數的傳回總是兩種情況,或者是default(使用者隻是按enter鍵),或者是一個有效的整數(大于或等于指定的minimum)。

通過我們的get_int()函數,可以很容易地擷取行數、列數以及使用者需要的最小随機數值。對于給定預設值none的行數與列數,也就是沒有指定預設值的情況,使用者必須輸入一個整數。對于minimum,我們提供的預設值為0:對于maximum,我們提供的預設值為1000或minimum的2倍(如果minimum大于或等于1000)。

與前面的例子類似,函數調用參數清單可以跨越任意數量的行數,并且縮排與第二行及後繼行無關。

在确定使用者需要的具體行數、列數以及随機數的最大值與最小值後,就可以進行具體的随機數生成過程:

為生成随機數網格,我們使用3個while循環,外部循環以行數進行循環,中間循環以列數進行循環,内部循環則以字元進行循環。在中間循環中,我們擷取指定範圍内的随機數,并将其轉換為字元串。内部while循環用于對字元串進行填充(填充資料為空格),以便每個數字都使用10個字元的字元串表示,對每一行,使用字元串line來累積數值,在每一列的數字添加完畢後,就列印出該行表示的數字,至此,第二個程式功能講解完畢。

python提供了非常進階的格式化功能,以及對for ... in循環的良好支援能力,是以,bigdigits.py與generate_grid.py這兩個程式的更真實的版本會使用for ... in循環,generate_grid.py程式将使用python的字元串格式化功能,而不是像這裡這樣不帶修飾地使用空格進行填充。但是在本章中,我們将自己限制在使用本章介紹的8個關鍵要素進行程式設計,并且這8個要素對編寫完整而有用的程式也已足夠。在接下來的每一章中,我們都将學習python的一些新特性,是以,随着本書内容的推進,所看到的程式将逐漸複雜起來。