19 面向過程程式設計與函式式
程式設計正規化
面向過程
函式式
- 匿名函式與lambda
- map、reduce、filter
一、程式設計正規化
很多初學者在瞭解了一門程式語言的基本語法和使用之後,面對一個’開發需求‘時仍然會覺得無從下手、沒有思路/套路,本節主題“程式設計正規化”正是為了解決該問題,那到底什麼是程式設計正規化呢?
程式設計正規化指的就是程式設計的套路,打個比方,如果把程式設計的過程比喻為練習武功,那程式設計正規化指的就是武林中的各種流派,而在程式設計的世界裡常見的流派有:面向過程、函式式、面向物件等,本節我們主要介紹面向過程與函式式。
在正式介紹前,我們需要強調:“功夫的流派沒有高低之分,只有習武的人才有高低之分“,在程式設計世界裡更是這樣,各種程式設計正規化在不同的場景下都各有優劣,誰好誰壞不能一概而論,下面就讓我們來一一解讀它們。
二、面向過程
”面向過程“核心是“過程”二字,“過程”指的是解決問題的步驟,即先幹什麼再幹什麼......,基於面向過程開發程式就好比在設計一條流水線,是一種機械式的思維方式,這正好契合計算機的執行原理:任何程式的執行最終都需要轉換成cpu的指令流水按過程排程執行,即無論採用什麼語言、無論依據何種程式設計正規化設計出的程式,最終的執行都是過程式的。
詳細的,若程式一開始是要著手解決一個大的問題,按照過程式的思路就是把這個大的問題分解成很多個小問題或子過程去實現,然後依次呼叫即可,這極大地降低了程式的複雜度。舉例如下:
寫一個數據遠端備份程式,分三步:本地資料打包,上傳至雲伺服器,檢測備份檔案可用性
import os,time # 一:基於本章所學,我們可以用函式去實現這一個個的步驟 # 1、本地資料打包 def data_backup(folder): print("找到備份目錄: %s" %folder) print('正在備份...') zip_file='/tmp/backup_%s.zip' %time.strftime('%Y%m%d') print('備份成功,備份檔案為: %s' %zip_file) return zip_file #2、上傳至雲伺服器 def cloud_upload(file): print("\nconnecting cloud storage center...") print("cloud storage connected") print("upload [%s] to cloud..." %file) link='https://www.xxx.com/bak/%s' %os.path.basename(file) print('close connection') return link #3、檢測備份檔案可用性 def data_backup_check(link): print("\n下載檔案: %s , 驗證檔案是否無損..." %link) #二:依次呼叫 # 步驟一:本地資料打包 zip_file = data_backup(r"/Users/cx/歐美100G高清無碼") # 步驟二:上傳至雲伺服器 link=cloud_upload(zip_file) # 步驟三:檢測備份檔案的可用性 data_backup_check(link)
面向過程總結:
優點
將複雜的問題流程化,進而簡單化
缺點
'''
程式的可擴充套件性極差,因為一套流水線或者流程就是用來解決一個問題,就好比生產汽水的流水線無法生產汽車一樣,即便是能,也得是大改,而且改一個元件,與其相關的元件可能都需要修改,比如我們修改了cloud_upload的邏輯,那麼依賴其結果才能正常執行的data_backup_check也需要修改,這就造成了連鎖反應,而且這一問題會隨著程式規模的增大而變得越發的糟糕。
'''
def cloud_upload(file): # 加上異常處理,在出現異常的情況下,沒有link返回
try:
print("\nconnecting cloud storage center...")
print("cloud storage connected")
print("upload [%s] to cloud..." %file)
link='https://www.xxx.com/bak/%s' %os.path.basename(file)
print('close connection')
return link
except Exception:
print('upload error')
finally:
print('close connection.....')
def data_backup_check(link): # 加上對引數link的判斷
if link:
print("\n下載檔案: %s , 驗證檔案是否無損..." %link)
else:
print('\n連結不存在')
應用場景
面向過程的程式設計一般用於那些功能一旦實現之後就很少需要改變的場景, 如果你只是寫一些簡單的指令碼,去做一些一次性任務,用面向過程去實現是極好的,但如果你要處理的任務是複雜的,且需要不斷迭代和維護, 那還是用面向物件最為方便。
三、函式式
函數語言程式設計並非用函式程式設計那麼簡單,而是將計算機的運算視為數學意義上的運算,比起面向過程,函式式更加註重的是執行結果而非執行的過程,代表語言有:Haskell、Erlang。而python並不是一門函數語言程式設計語言,但是仍為我們提供了很多函數語言程式設計好的特性,如lambda,map,reduce,filter
匿名函式與lambda
對比使用def關鍵字建立的是由名字的函式,使用lambda關鍵字建立則是沒有名字的函式,即匿名函式,語法如下
lambda 引數1,引數2,...:expression
舉例
# 1.定義
lambda x,y,z:x+y+z
# 等同於
def func(x,y,z):
return x+y+z
# 2.呼叫
# 方式一
res = lambda x,y,z:x+y+z # “匿名”的本質就是要沒有名字,所以此處為匿名函式指定名字是沒有意義的
res = func(1,2,3)
匿名函式與有名函式有相同的作用域,但是匿名意味著引用計數為0,使用一次就釋放,所以匿名函式用於臨時使用一次的場景,匿名函式通常與其他函式配合使用,我們以下述字典為例來介紹它
salaries={
'siry':3000,
'tom':7000,
'lili':10000,
'jack':2000
}
要想取得薪水最大值和最小值,我們可以使用內建函式max和min(為了方便開發,python直譯器已經為我們定義好了一系列常用的功能,稱之為內建的函式,我們只需要拿來使用即可)
>>> max(salaries)
'tom'
>>> min(salaries)
'jack'
內建max和min都支援迭代器協議,工作原理都是迭代字典,取得是字典的鍵,因而比較的是鍵的最大和最小值,而我們想要的是比較值的最大值與最小值,於是做出如下改動
# 函式max會迭代字典salaries,每取出一個“人名“就會當作引數傳給指定的匿名函式,然後將匿名函式的返回值當作比較依據,最終返回薪資最高的那個人的名字
>>> max(salaries,key = lambda k:salaries[k])
'lili'
>>> min(salaries,key = lambda k:salaries[k])
'jack'
同理,我們直接對字典進行排序,預設也是按照字典的鍵去排序的
>>> sorted(salaries)
['jack', 'lili', 'siry', 'tom']
map、reduce、filter
函式map、reduce、filter都支援迭代器協議,用來處理可迭代物件,我們以一個可迭代物件array為例來介紹它們三個的用法
array = [1, 2, 3, 4, 5]
要求一:對array的每個元素做平方處理,可以使用map函式
map函式可以接收兩個引數,一個是函式,另外一個是可迭代物件,具體用法如下
>>> res = map(lambda x:x**2,array)
>>> res
<map object at 0x000001BBFE072790>
解析:map會依次迭代array,得到的值依次傳給匿名函式(也可以是有名函式),而map函式得到的結果仍然是迭代器。
>>> list(res)
[1, 4, 9, 16, 25]
要求二:對array進行合併操作,比如求和運算,這就用到了reduce函式
reduce函式可以接收三個引數,一個是函式,第二個是可迭代物件,第三個是初始值
# reduce在python2中是內建函式,在python3中則被整合到模組functools中,需要匯入才能使用
>>> from functools import reduce
>>> res = reduce(lambda x,y:x+y,array)
>>> res
15
解析:
1.沒有初始值,reduce函式會先迭代依次array,得到的值作為初始值,作為第一個值數傳給x,然後繼續迭代依次array得到的值作為第二個值傳給y,運算的結果為3
2.將上一次reduce運算的結果作為第一個值傳給x,然後迭代一次array得到的結果作為第二個值傳給y,以此類推,直到迭代完array所有元素,得到最終的結果15
也可以為reduce指定初始值
>>> res = reduce(lambda x,y:x+y,array,100)
>>> res
115
要求三:對array進行過濾操作,這就用到了filter函式,比如過濾出大於3的元素
>>> res = filter(lambda x:x>3,array)
解析:filter函式會依次迭代array,得到的值依次傳給匿名函式,如果匿名函式的返回值為真,則過濾出該元素,而filter函式得到的結果仍然是迭代器
>>> list(res)
[4, 5]
提示:我們介紹map、filter、reduce只是為了帶大家瞭解函數語言程式設計的大致思想,在實際開發中,我們完全可以用列表生成式或者生成器表示式來實現三者功能