Python函數式編程 TOC + 函數參數中的魔法 + 位置參數 + 預設參數 + 動態參數 + 位置參數收集 + 關鍵字參數收集 + 收集參數行為的逆向過程(這個逆向過程用於實參上) + 參數傳遞 + 函數嵌套 + 命名空間 + 作用域 + 閉包 + 函數式編程 + 裝飾器 + 顯示方式 + @ ...
Python函數式編程
TOC
- 函數參數中的魔法
- 位置參數
- 預設參數
- 動態參數
- 位置參數收集
- 關鍵字參數收集
- 收集參數行為的逆向過程(這個逆向過程用於實參上)
- 參數傳遞
- 函數嵌套
- 命名空間
- 作用域
- 閉包
- 函數式編程
- 裝飾器
- 顯示方式
- @方式
- 帶參數裝飾器
- functools.wraps(func)
- 匿名函數
- 高階函數
- 三劍客
- filter()
- map()
- reduce()
- sorted()
- 三劍客
- 遞歸函數
函數參數中的魔法
coming...
嵌套函數
顧名思義就是一個函數定義中嵌套了一個函數的定義。說道嵌套函數,就必須先介紹命名空間和作用域
命名空間NameSpace
python分三類命名空間。內置,全局,本地。包括一個區域,封閉區域。
- Builtins 內置空間,在main模塊載入前創建
- Globals 全局空間,模塊為單位。包空間其實是__init__.py模塊
- Enclosing 封閉區域
- Locals 本地空間,函數,類,對象等
作用域Scope
python對象的作用域是遵照LEGB規則。
閉包
一般普通的函數嵌套並沒什麼特別的用處。但是嵌套函數有個突出的應用,也是非常強大的應用。就是,通過外層函數創建一個函數,即返回內層函數,外層函數每一調用返回的內層函數,這些返回的內層函數之間是獨立的函數對象。他們都“繼承”了外層函數調用時的作用域。這個“繼承”行為我們稱為閉包。
閉包顯著的特點就是:返回的內層函數中,使用了外層函數的局部變數或者參數參與內層函數的運算。這些變數和參數的值還是外部函數調用返回前時的值。
要註意的是:返回內層函數時,使用外層函數時的變數或者參數,如果在返回後發生了變化。那麼內層函數後面調用會使用變化後的值,而不是返回內層函數那個時間點的值了。所以很多博客都在強調,閉包返回的內層函數不要引用還會在外層函數發生變化的變數。
要解決這個問題,可以採取中間變數(中間變數就是固定的了)或者再嵌套一個中間函數,讓這個中間函數來獲取變化的變數,在傳遞給最內層的函數,並返回最內層的函數。
記住:內層函數返回時是不會執行的。
__自己猜想__
有點類似於面向對象,一個對象方法返回另一個對象,新對象的實例化是受到返回它的對象影響,展現除了多態的特性。閉包有點多態,繼承,封裝(函數本來就是一種封裝形式)的意思,將規律相似的作為一層。多層次之間可以看作多層次的嵌套函數來設計,能產生“繼承變數”的哪一層就作為上層,有幾層規律就嵌套幾層函數,從外到內,每一層都是和上一層有一個或多個“繼承變數”關係。算一種設計範式了吧。
如:乘法運算,將第一個乘數作為一層,第二乘數提供後算出乘積來作為第二層。那麼就可以使用閉包將拆分有關係的兩層進行程式設計。
再如:裝飾器函數的inner()函數,就是要繼承裝飾函數說傳遞的參數。返回的inner函數調用就會間接調用被裝飾的函數。inner函數實現了擴展需求的功能。
找點裝飾器或者其它閉包例子來佐證猜想
函數式編程
摘自廖雪峰python教程
函數式編程是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數是沒有變數的,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變數的程式設計語言,由於函數內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此這種函數是有副作用的。
函數式編程的一個特點就是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
python 對函數式編程提供部分支持。由於Python允許使用變數,因此Python不是純函數式編程語言。
裝飾器Decorator
裝飾器是函數式編程的集大成應用。涉及高階函數,閉包,參數傳遞等。
如果只是要擴展功能,只需要再創建一個函數,並嵌套需要擴展的函數,就是在創建的函數中實現擴展功能後,再執行擴展前的函數就可以了。但是這樣做有一個缺點就是,原函數名被改變了,這樣會被調用原函數的其它部門打死的,你這樣方便了自己卻麻煩了其他人。
那就要想辦法呀,那重命名這個擴展的函數為原函數名呢?肯定不行,這樣函數調用就編程了遞歸函數了。
那就這樣設計:用一個函數返回一個函數,返回的函數重命名為原來的函數名。調用這個重命名返回函數,就是執行擴展後再調用原函數。這就是通過閉包,實現裝飾器。
- 裝飾外層函數:返回內層函數
- 裝飾內層函數:實現原功能上的“封閉-開發”原則的擴展功能。內層函數對擴展功能實現後,再調用原函數,並return原函數調用返回值。
顯示方式
# 我們已一個廖老師裝飾器思考題為例,說明兩種方式表示裝飾器的應用
# 題目:請設計一個decorator,它可作用於任何函數上,並列印該函數的執行時間
import time
def runtime(func):
def wrapper(*args, **kwargs):
t1 = time.time()
res = func(*args, **kwargs)
t2 = time.time()
print((t2-t1))
return res
return wrapper
def get_sum(*args):
sums = 0
for i in args:
sums += i
return sums
get_sum = runtime(get_sum) # 這就是顯示方式
print(get_sum(*list(range(10000))))
@方式
# 裝飾器函數一樣
import time
def runtime(func):
def wrapper(*args, **kwargs):
t1 = time.time()
res = func(*args, **kwargs)
t2 = time.time()
print((t2-t1))
return res
return wrapper
@runtime # 這裡就是@方式,等價於上面的 顯示方式
def get_sum(*args):
sums = 0
for i in args:
sums += i
return sums
print(get_sum(*list(range(10000))))
結果:
0.0010001659393310547 #運行時間
49995000 #函數返回值
使用裝飾器擴展的功能變得更具相容性,高內聚。(帶參數裝飾器)
什麼意思呢?翻譯成‘人話’就是:一般裝飾器只擴展一類相似功能的一種,現在要給這一功能提供並傳遞參數,是裝飾器展現出不同的行為。
裝飾器帶上參數,只需要再裝飾器包裹一個函數,這個函數接收參數,返回一個外層裝飾器函數,內層函數接受到包裹的哪層函數的參數,在擴展代碼中進行應用,這樣使得擴展代碼具有更多變數,理所當然,也會拿到外層還是傳遞的原函數。內層函數拿到這兩個東西,就可以大施拳腳,做出各種功能來了。
直接代碼說話:
# 廖老師的列印調用一個函數的日誌。
def log(text):
def outer(func):
def inner(*args, **kwargs):
print("%s begin call " % text)
return func(*args, **kwargs)
return inner
retrun outer
@log('求和')
def get_sum(*args):
sums = 0
for i in args:
sums += i
return sums
print(get_sum(*list(range(10000))))
運行結果:
求和 begin call # 擴展的功能:列印執行日誌
49995000 # 函數返回結果
讓裝飾器更安全
因為裝飾器的內層函數被重新綁定給了原函數名,內層函數的__name__還是內層函數名,而不是原函數名。有些依賴函數簽名的代碼可能會報錯。python的functools提供了
將內層函數的__name__值變為重新綁定到的變數的名字。
用法:
import functools
def decorator(func)
@functools.wraps(func) # 用在這裡,就是wrapper之前,無論帶參數還是不帶參數的裝飾器
def wrapper(*args, **kwargs):
pass
return func(*args, **kwargs)
return wrapper
裝飾器還可以不止一個參數,多個參數,多層包裹,產生多個維度。
匿名函數
匿名函數就是不需要顯示的通過def定義函數。匿名函數通常用於高階函數。匿名函數直接用於高階函數的參數或者return中,如果在return中,就形成了閉包。
關鍵字lambda 代表匿名函數
匿名函數表示方法:
lambda x, y : x + y
冒號前x, y是參數列表,冒號後面表達式的值作為函數的return值,且冒號後面只能是一個表達式,不能包含其它語句。如賦值語句,複合語句。可以使用三元表達式。
匿名函數可以縮短代碼行數。
匿名函數例子:
L = list(map(lambda *args: lambda *innerargs: print(innerargs+args),[1,2,3,4,5,6,7]))
for f in L:
f(10)
結果:
(10, 1)
(10, 2)
(10, 3)
(10, 4)
(10, 5)
(10, 6)
(10, 7)
高階函數
什麼是高階函數?
coming...
遞歸函數
coming....