1. 程式人生 > >第8章:Python計算生態

第8章:Python計算生態

註明:本系列課程專為全國計算機等級考試二級 Python 語言程式設計考試服務

目錄

考綱考點

知識導圖

1、計算思維

2、程式設計方法論

自頂向下設計

3、自底向上執行

4、計算生態

Python標準庫

Python第三方庫

5、基本的Python內建函式

6、例項解析:Web頁面元素提取

本章小結


考綱考點

  • 基本的Python內建函式
  • 瞭解Python計算生態

知識導圖

1、計算思維

  • 人類在認識世界、改造世界過程中表現出三種基本的思維特徵:以實驗和驗證為特徵的實證思維,以物理學科為代表;以推理和演繹為特徵的邏輯思維,以數學學科為代表;以設計和構造為特徵的計算思維,以計算機學科為代表。
  • 計算思維的本質是抽象(Abstraction)和自動化(Automation)

2、程式設計方法論

  • 一個解決複雜問題行之有效的方法被稱作自頂而下的設計方法,其基本思想是以一個總問題開始,試圖把它表達為很多小問題組成的解決方案。再用同樣的技術依次攻破每個小問題,最終問題變得非常小,以至於可以很容易解決。然後只需把所有的碎片組合起來,就可以得到一個程式。

 “體育競技分析”例項

  • 兩個球員在一個有四面邊界的場地上用球拍擊球。開始比賽時,其中一個球員首先發球。接下來球員交替擊球,直到可以判定得分為止,這個過程稱為回合。當一名球員未能進行一次合法擊打時,回合結束。
  • 未能打中球的球員輸掉這個回合。如果輸掉這個回合的是發球方,那麼發球權交給另一方;如果輸掉的是接球方,則仍然由這個回合的發球方繼續發球。
  • 總之,每回合結束,由贏得該回合的一方發球。球員只能在他們自己的發球局中得分。首先達到15分的球員贏得一局比賽。

自頂向下設計

  • 自頂向下設計中最重要的是頂層設計。體育競技分析從使用者處得到模擬引數,最後輸出結果。下面是一個基礎設計:

• 步驟1: 列印程式的介紹性資訊;

• 步驟2:獲得程式執行需要的引數:probA, probB, n;

• 步驟3:利用球員A和B的能力值probA和probB,模擬n次比賽;

• 步驟4:輸出球員A和B獲勝比賽的場次及概率。

  • 步驟1  輸出一些介紹資訊,針對提升使用者體驗十分有益。下面是這個步驟的Python程式碼,頂層設計一般不寫出具體程式碼,僅給出函式定義,其中,printIntro()函式列印一些必要的說明。
def main():
    printIntro()
  • 步驟2 獲得使用者輸入。通過函式將輸入語句及輸入格式等細節封裝或隱藏,只需要假設程式如果呼叫了getInputs()函式即可獲取變數probA,probB和n的值。這個函式必須為主程式返回這些值,截止第2步,全部程式碼如下。
def main():
    printIntro()
    probA, probB, n = getInputs()
  • 步驟3 需要使用probA、probB模擬n場比賽。此時,可以採用解決步驟2的類似方法,設計一個simNGames()函式來模擬n場比賽
def main():
    printIntro()
    probA, probB, n = getInputs()
    winsA, winsB = simNGames(n, probA, probB)
  • 步驟4 輸出結果,設計思想類似,仍然只規劃功能和函式,程式碼如下。
def main():
    printIntro()
    probA, probB, n = getInputs()
    winsA, winsB = simNGames(n, probA, probB)
    printSummary(winsA, winsB)
  • 原問題被劃分為了4個獨立的函式:printIntro(),getInputs(),simNGames()和printSummary()。

  • 每層設計中,引數和返回值如何設計是重點,其他細節可以暫時忽略。確定事件的重要特徵而忽略其它細節過程稱為抽象。抽象是一種基本設計方法,自頂向下的設計過程可以看作是發現功能並抽象功能的過程。
  • printIntro()函式應該輸出一個程式介紹,這個功能的Python程式碼如下,這個函式由Python基本表示式組合,不增加或改變程式結構。
def printIntro():
    print("這個程式模擬兩個選手A和B的某種競技比賽")
    print("程式執行需要A和B的能力值(以0到1之間的小數表示)")
  • getInputs()函式根據提示得到三個需要返回主程式的值,程式碼如下。
def getInputs():
    a = eval(input("請輸入選手A的能力值(0-1): "))
    b = eval(input("請輸入選手B的能力值(0-1): "))
    n = eval(input("模擬比賽的場次: "))
    return a, b, n
  • simNGames()函式是整個程式的核心,其基本思路是模擬n場比賽,並跟蹤記錄每個球員贏得了多少比賽。
def simNGames(n, probA, probB):
    winsA, winsB = 0, 0
    for i in range(n):
        scoreA, scoreB = simOneGame(probA, probB)
        if scoreA > scoreB:
            winsA += 1
        else:
            winsB += 1
    return winsA, winsB
  • 程式碼中設計了simOneGame()函式,用於模擬一場比賽,這個函式需要知道每個球員的概率,返回兩個球員的最終得分

  • 接下來需要實現simOneGame()函式。
  • 為了模擬一場比賽,需要根據比賽規則來編寫程式碼,兩個球員A和B持續對攻直至比賽結束。可以採用無限迴圈結構直到比賽結束條件成立。同時,需要跟蹤記錄比賽得分,保留髮球局標記,
  • 在模擬比賽的迴圈中,需要考慮單一的發球權和比分問題,通過隨機數和概率,可以確定發球方是否贏得了比分(random() < prob)。如果球員A發球,那麼需要使用A的概率,接著根據發球結果,更新球員A得分或是將球權交給球員B。
def simOneGame(probA, probB):
    scoreA, scoreB = 0, 0
    serving = "A"
    while not gameOver(scoreA, scoreB):
        if serving == "A":
            if random() < probA:
                scoreA += 1
            else:
                serving="B"
        else:
            if random() < probB:
                scoreB += 1
            else:
                serving="A"
    return scoreA, scoreB

  • 根據比賽規則,當任意一個球員分數達到15分時比賽結束。gameOver()函式實現程式碼如下。
def gameOver(a,b):
    return a==15 or b==15
  • 最後是printSummary()函式,其Python程式碼如下。
def printSummary(winsA, winsB):
    n = winsA + winsB
    print("競技分析開始,共模擬{}場比賽".format(n))
    print("選手A獲勝{}場比賽,佔比{:0.1%}".format(winsA, winsA/n))
    print("選手B獲勝{}場比賽,佔比{:0.1%}".format(winsB, winsB/n))
  • 將上述所有程式碼放在一起,形成了例項全部程式碼。執行結果如下:
>>>
這個程式模擬兩個選手A和B的某種競技比賽
程式執行需要A和B的能力值(以0到1之間的小數表示)
請輸入選手A的能力值(0-1): 0.45
請輸入選手B的能力值(0-1): 0.5
模擬比賽的場次: 1000
競技分析開始,共模擬1000場比賽
選手A獲勝371場比賽,佔比37.1%
選手B獲勝629場比賽,佔比62.9%
  • 結合體育競技例項介紹了自頂向下的設計過程。從問題輸入輸出確定開始,整體設計逐漸向下進行。每一層以大體演算法描述開始,然後逐步細化成程式碼,細節被函式封裝

 整個過程可以概括為四個步驟:

  • 步驟1:將演算法表達為一系列小問題;
  • 步驟2:為每個小問題設計介面;
  • 步驟3:通過將演算法表達為介面關聯的多個小問題來細化演算法;
  • 步驟4:為每個小問題重複上述過程。

3、自底向上執行

  • 開展測試的更好辦法也是將程式分成小部分逐個測試
  • 執行中等規模程式的最好方法是從結構圖最底層開始,而不是從頂部開始,然後逐步上升。或者說,先執行和測試每一個基本函式,再測試由基礎函式組成的整體函式,這樣有助於定位錯誤
  • 可以從gameOver()函式開始測試。Python 直譯器提供import 保留字輔助開展單元測試,語法格式如下:

import <原始檔名稱>

>>>import MatchAnalysis
>>>MatchAnalysis.gameOver(15, 10)
True
>>>MatchAnalysis.gameOver(10, 1)
False
  • 初步測試說明gameOver()函式是正確的。進一步測試simOneGame()函式,如下:
>>>import e151MatchAnalysis
>>>e151MatchAnalysis.simOneGame(.45, .5)
(9, 15)
>>>e151MatchAnalysis.simOneGame(.45, .5)
(15, 13)
  • 通過繼續進行這樣的單元測試可以檢測程式中的每個函式。獨立檢驗每個函式更容易發現錯誤。通過模組化設計可以分解問題使編寫複雜程式成為可能,通過單元測試方法分解問題使執行和除錯複雜程式成為可能。
  • 自頂向下和自底向上貫穿程式設計和執行的整個過程。

4、計算生態

  • 近20年的開源運動產生了深植於各資訊科技領域的大量可重用資源,直接且有力的支撐了資訊科技超越其他技術領域的發展速度,形成了“計算生態”
  • Python語言從誕生之初致力於開源開放,建立了全球最大的程式設計計算生態。
  • Python官方網站提供了第三方庫索引功能(PyPI,the Python Package Index),網址如下:

https://pypi.python.org/pypi

  • 該頁面列出了Python語言超過12萬個第三方庫的基本資訊,這些函式庫覆蓋資訊領域技術所有技術方向。
  • 由於Python有非常簡單靈活的程式設計方式,很多采用C、C++等語言編寫的專業庫可以經過簡單的介面封裝供Python語言程式呼叫。這樣的粘性功能使得Python語言成為了各類程式語言之間的介面,Python語言也被稱為“膠水語言”。

Python標準庫

  • 有一部分Python計算生態隨Python安裝包一起釋出,使用者可以隨時使用,被稱為Python標準庫
  • 受限於Python安裝包的設定大小,標準庫數量270個左右。

Python第三方庫

  • 更廣泛的Python計算生態採用額外安裝方式服務使用者,被稱為Python第三方庫。這些第三方庫由全球各行業專家、工程師和愛好者開發,沒有頂層設計,由開發者採用“盡力而為”的方式維護。Python通過新一代安裝工具pip管理大部分Python第三方庫的安裝。

5、基本的Python內建函式

  • 官方教材上使用的版本說Python直譯器提供了68個內建函式(下面介紹31個)
  • 所有內建函式的詳細介紹以及舉例請查閱 -> Python內建函式查詢
函式名稱 函式說明
abs(x) x的絕對值,如果x是複數,返回複數的模
all(x) 組合型別變數x中所有元素都為真時返回True,否則返回False;若x為空,返回True
any(x) 組合型別變數x中任一元素都為真時返回True,否則返回False;若x為空,返回False
bin(x) 將整數x轉換為等值的二進位制字串,bin(1010)的結果是'0b1111110010'
bool(x) 將x轉換為Boolean型別,即True或False,bool('') 的結果是False
chr(i) 返回Unicode為i的字元,chr(9996)的結果是'✌'
complex(r,i) 建立一個複數 r + i*1j,其中i可以省略,complex(10,10)的結果是10+10j
dict() 建立字典型別,dict()的結果是一個空字典{}
divmod(a,b) 返回a和b的商及餘數,divmod(10,3)結果是一個(3,1)
eval(s) 計算字串s作為Python表示式的值,eval('1+99')的結果是100
exec(s) 計算字串s作為Python語句的值,exec('a = 1+999')執行後,變數a的值為1000
float(x) 將x轉換成浮點數,float(1010)的結果是1010.0
hex(x) 將整數轉換為16進位制字串,hex(1010)的結果是'0x3f2'
input(s) 獲取使用者輸入,其中s是字串,作為提示資訊,s可選
int(x) 將x轉換成整數,int(9.9)的結果是9
list(x) 建立或將變數x轉換成一個列表型別,list({10,9,8})的結果是[8,9,10]
max(a1,a2,…) 返回引數的最大值,max(1,2,3,4,5)的結果是5
min(a1,a2,…) 返回引數的最小值,min(1,2,3,4,5)的結果是1
oct(x) 將整數x轉換成等值的八進位制字串形式,oct(1010)的結果是'0o1762'
open(fname, m) 開啟檔案,包括文字方式和二進位制方式等,其中,m部分可以省略,預設是以文字可讀形式開啟
ord(c) 返回一個字元的Unicode編碼值,ord('字')的結果是23383
pow(x,y) 返回x的y次冪,pow(2,pow(2,2))的結果是16
print(x) 列印變數或字串x,print()的end引數用來表示輸出的結尾字元
range(a,b,s) 從a到b(不含)以s為步長產生一個序列,list(range(1,10,3))的結果是[1, 4, 7]
reversed(r) 返回組合型別r的逆序迭代形式,for i in reversed([1,2,3])將逆序遍歷列表
round(n) 四捨五入方式計算n,round(10.6)的結果是11
set(x) 將組合資料型別x轉換成集合型別,set([1,1,1,1])的結果是{1}
sorted(x) 對組合資料型別x進行排序,預設從小到大,sorted([1,3,5,2,4])的結果是[1,2,3,4,5]
str(x) 將x轉換為等值的字串型別,str(0x1010)的結果是'4112'
sum(x) 對組合資料型別x計算求和結果,sum([1,3,5,2,4])的結果是15
type(x) 返回變數x的資料型別,type({1:2})的結果是<class 'dict'>

6、例項解析:Web頁面元素提取

  • Web頁面,一般是HTML頁面,是Internet組織資訊的基礎元素。Web頁面元素提取是一類常見問題,在網路爬蟲、瀏覽器等程式中有著不可或缺的重要作用。
  • HTML指超文字標記語言,嚴格來說,HTML不是一種程式語言,而是一種對資訊的標記語言,對Web的內容、格式進行描述。
  • 自動地從一個連結獲取HTML頁面是網路爬蟲的功能,本例項功能可以整體分成如下4個步驟:

• 步驟1: 讀取儲存在本地的html檔案;

• 步驟2:解析並提取其中的圖片連結;

• 步驟3:輸出提取結果到螢幕;

• 步驟4:儲存提取結果為檔案。

  • 根據上述步驟,可以寫出主程式如下。其中設定了4個函式getHTMLlines()、extractImageUrls()、showResults()和saveResults()分別對應上述4個步驟。
def main():
    inputfile = 'nationalgeographic.html'
    outputfile = 'nationalgeographic-urls.txt'
    htmlLines = getHTMLlines(inputfile)
    imageUrls = extractImageUrls(htmlLines)
    showResults(imageUrls)
    saveResults(outputfile, imageUrls)
  • 定義main()函式的目的是為了讓程式碼更加清晰,作為主程式,也可以不採用函式形式而直接編寫。main()前兩行分別制定了擬獲取HTML檔案的路徑和結果輸出路徑。
  • 主函式設計完成後,逐一編寫各函式功能。
  • getHTMLlines()函式讀取HTML檔案並內容,並將結果轉換為一個分行列表,為了相容不同編碼,建議在open()函式中增加encoding欄位,設定採用UTF-8編碼開啟檔案。程式碼如下。
def getHTMLlines(htmlpath):
    f = open(htmlpath, "r", encoding='utf-8')
    ls = f.readlines()
    f.close()
    return ls
  • extractImageUrls()是程式的核心,用於解析檔案並提取影象的URL。觀察HTML可以發現,影象採用img標籤表示,例如:

<img title="photo story"
src="http://image.nationalgeographic.com.cn/2018/0122/20
180122042251164.jpg" width="968px" />

  • 其中,<img開頭是影象標籤的特點,其中由src=所引導的URL是這個影象的真實位置。每個URL都以http開頭。因此,可以通過字串操作提取其中的影象連結。
def extractImageUrls(htmllist):
    urls = []
    for line in htmllist:
        if 'img' in line:
            url = line.split('src=')[-1].split('"')[1]
            if 'http' in url:
                urls.append(url)
    return urls
  • showResults()函式將獲取的連結輸出到螢幕上,增加一個計數變數提供更好使用者體驗,程式碼如下。
def showResults(urls):
    count = 0
    for url in urls:
        print('第{:2}個URL:{}'.format(count, url))
        count += 1
  • saveResults()儲存結果到檔案,程式碼如下。
def saveResults(filepath, urls):
    f = open(filepath, "w")
    for url in urls:
        f.write(url+"\n")
    f.close()
  • 各部分函式程式碼編寫後,全部程式碼功能具備,需要額外呼叫main()函式
def getHTMLlines(htmlpath):
    f = open(htmlpath, "r", encoding='utf-8')
    ls = f.readlines()
    f.close()
    return ls

def extractImageUrls(htmllist):
    urls = []
    for line in htmllist:
        if 'img' in line:
            url = line.split('src=')[-1].split('"')[1]
            if 'http' in url:
                urls.append(url)
    return urls
    
def showResults(urls):
    count = 0
    for url in urls:
        print('第{:2}個URL:{}'.format(count, url))
        count += 1
    
def saveResults(filepath, urls):
    f = open(filepath, "w")
    for url in urls:
        f.write(url+"\n")
    f.close()
    
def main():
    inputfile  = 'nationalgeographic.html'
    outputfile = 'nationalgeographic-urls.txt'
    htmlLines = getHTMLlines(inputfile)
    imageUrls = extractImageUrls(htmlLines)
    showResults(imageUrls)
    saveResults(outputfile, imageUrls)

main()
>>> 

第 0個URL:http://image.nationalgeographic.com.cn/2018/0122/20180122042251164.jpg
第 1個URL:http://image.nationalgeographic.com.cn/2018/0122/20180122120753804.jpg
第 2個URL:http://image.nationalgeographic.com.cn/2018/0122/20180122102058707.jpg
第 3個URL:http://image.nationalgeographic.com.cn/2018/0118/20180118124326995.jpg
第 4個URL:http://image.nationalgeographic.com.cn/2018/0116/20180116112407593.jpg
第 5個URL:http://image.nationalgeographic.com.cn/2018/0122/20180122035438691.jpg
第 6個URL:http://image.nationalgeographic.com.cn/2018/0118/20180118040311659.jpg
第 7個URL:http://image.nationalgeographic.com.cn/2018/0117/20180117022633730.jpg
第 8個URL:http://image.nationalgeographic.com.cn/2018/0115/20180115024334826.jpg
第 9個URL:http://image.nationalgeographic.com.cn/2018/0112/20180112025424524.png
第10個URL:http://image.nationalgeographic.com.cn/2018/0122/20180122105741158.jpg
#下面省去40個URL

本章小結

本章主要講解程式設計方法學,包括計算思維、自頂向下設計和自底向上執行等,進一步本章介紹了計算生態的概念及Python標準庫和第三方庫的劃分。通過Web頁面元素提取的例項幫助讀者理解自頂向下設計的基本方法。

從最基本的IPO到自頂向下設計,是否感受到了函數語言程式設計的優勢?