python-復盤總結-函數(shù)式編程

復盤廖雪峰教程中的要點

1. 函數(shù)式編程 > 高階函數(shù)



map() 函數(shù)接收兩個參數(shù)诵次,一個是函數(shù)翠拣,一個是Iterable,map將傳入的函數(shù)依次作用到序列的每個元素,并把結果作為新的Iterator 迭代器 返回滚朵。結果是一個惰性序列蛉拙,一般我們可以用list()把它變成列表讀取宙攻。

>>> def f(x):    # map 特別適合對內部每個元素都需要作用的地方画舌。
...     return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]



reduce()
reduce把一個函數(shù)作用在一個序列[x1, x2, x3, ...]上堕担,這個函數(shù)必須接收兩個參數(shù),reduce把結果繼續(xù)和序列的下一個元素做累積計算曲聂,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

應該適合在一些累計求和中去用霹购,得加這句from functools import reduce



fliter()
該函數(shù)為內置,和map()類似,filter()也接收一個函數(shù)和一個序列朋腋。和map()不同的是齐疙,filter()把傳入的函數(shù)依次作用于每個元素,然后根據返回值是True還是False決定保留還是丟棄該元素

def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結果: [1, 5, 9, 15]



sorted()
Python內置的sorted()函數(shù)就可以對list進行排序

>>> sorted([36, 5, -12, 9, -21], key=abs)   # 當一個參數(shù)時旭咽,可以將數(shù)字按大小排列贞奋,兩個值時,按照key排序轻专,這里abs是求絕對值
[5, 9, -12, -21, 36]



返回函數(shù)(難點) - 閉包
高階函數(shù)除了可以接受函數(shù)作為參數(shù)外忆矛,還可以把函數(shù)作為結果值返回。

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

當我們調用lazy_sum()時请垛,返回的并不是求和結果催训,而是求和函數(shù):

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>

調用函數(shù)f時,才真正計算求和的結果:

>>> f()
25

在這個例子中宗收,我們在函數(shù)lazy_sum中又定義了函數(shù)sum漫拭,并且,內部函數(shù)sum可以引用外部函數(shù)lazy_sum的參數(shù)和局部變量混稽,當lazy_sum返回函數(shù)sum時采驻,相關參數(shù)和變量都保存在返回的函數(shù)中,這種稱為“閉包(Closure)”的程序結構擁有極大的威力匈勋。

請再注意一點礼旅,當我們調用lazy_sum()時,每次調用都會返回一個新的函數(shù)洽洁,即使傳入相同的參數(shù):

>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False    #  f1()和f2()的調用結果互不影響

注意到返回的函數(shù)在其定義內部引用了局部變量args痘系,所以,當一個函數(shù)返回了一個函數(shù)后饿自,其內部的局部變量還被新函數(shù)引用汰翠,所以,閉包用起來簡單昭雌,實現(xiàn)起來可不容易复唤。

另一個需要注意的問題是,返回的函數(shù)并沒有立刻執(zhí)行烛卧,而是直到調用了f()才執(zhí)行佛纫。我們來看一個例子:

def count():
    fs = []
    for i in range(1, 4):     # 循環(huán)的函數(shù)
        def f():
             return i*i   # G:循環(huán)返回時,由于它優(yōu)先執(zhí)行,所以當循環(huán)1時呈宇,代碼不往下跟磨,而是繼續(xù)在內部直到循環(huán)到3,函數(shù)會再次賦值攒盈,形成新的函數(shù)
        fs.append(f)
    return fs

f1, f2, f3 = count()

結果如下:

>>> f1()    #  為什么結果不是1,4哎榴,9 型豁? 原因就在于返回的函數(shù)引用了變量i,但它并非立刻執(zhí)行尚蝌。等到3個函數(shù)都返回時迎变,它們所引用的變量i已經變成了3,因此最終結果為9
9    # G:個人理解當它循環(huán)1的時候飘言,返回的是一個函數(shù)衣形,當它循環(huán)到2時,已經變成了另一個函數(shù)姿鸿,雖然函數(shù)表達式一樣谆吴,但已經不是一家人了
>>> f2()   # 所以,當它循環(huán)3時苛预,這時又是一個函數(shù)句狼,它內部只有3這個值,結果當然是9
9
>>> f3()
9
>>> f5, f6 = count()
ValueError: too many values to unpack (expected 2)
>>> f7 = count()
>>> f7()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-183-6e06e95ee15c> in <module>()
----> 1 f7()

TypeError: 'list' object is not callable

>>> f7           # ??從這里可以看出count()就是三個函數(shù)組成的 list 啊~
[<function __main__.count.<locals>.f>,
 <function __main__.count.<locals>.f>,
 <function __main__.count.<locals>.f>]

教訓:返回閉包時牢記的一點就是:返回函數(shù)不要引用任何循環(huán)變量热某,或者后續(xù)會發(fā)生變化的變量腻菇。

如果一定要引用循環(huán)變量怎么辦?方法是再創(chuàng)建一個函數(shù)昔馋,用該函數(shù)的參數(shù)綁定循環(huán)變量當前的值筹吐,無論該循環(huán)變量后續(xù)如何更改,已綁定到函數(shù)參數(shù)的值不變:

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i))    # f(i)立刻被執(zhí)行秘遏,因此i的當前值被傳入f()
    return fs

再看看結果:

>>> f1, f2, f3 = count()      # ??count()應該是一個函數(shù)組成的list,所以必須用f1,f2,f3這樣對應數(shù)量的變量來實現(xiàn)拆包丘薛,否則即報錯
>>> f1()    
1
>>> f2()
4
>>> f3()
9
>>> f4 = count()              # ??思考一下,下面的變化
>>> f4                        # ??從這里可以看出count()就是三個函數(shù)組成的 list 啊~
[<function __main__.count.<locals>.f.<locals>.g>,
 <function __main__.count.<locals>.f.<locals>.g>,
 <function __main__.count.<locals>.f.<locals>.g>]

>>> f4()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-168-6e06e95ee15c> in <module>()
----> 1 f4()

TypeError: 'list' object is not callable

>>> f1
1

以上再??注意一點垄提,上述2段代碼榔袋, fs.append(f) fs.append(f(i)),對于函數(shù)f(x)來說,引用f(x)就是完整的函數(shù)式铡俐,跟數(shù)學一樣凰兑,會立即執(zhí)行,而引用 f审丘,這里只是一個指代函數(shù)的變量吏够,所以不會立即執(zhí)行,而是等待執(zhí)行順序優(yōu)先的參數(shù)再次改變它,形成新函數(shù)锅知。f(x)一個返回值播急,f一個是返回的函數(shù)



匿名函數(shù) lambda
當我們在傳入函數(shù)時,有些時候售睹,不需要顯式地定義函數(shù)桩警,直接傳入匿名函數(shù)更方便

>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

關鍵字lambda表示匿名函數(shù),冒號前面的x表示函數(shù)參數(shù)昌妹。

匿名函數(shù)有個限制捶枢,就是只能有一個表達式,不用寫return飞崖,返回值就是該表達式的結果烂叔。

用匿名函數(shù)有個好處,因為函數(shù)沒有名字固歪,不必擔心函數(shù)名沖突蒜鸡。此外,匿名函數(shù)也是一個函數(shù)對象牢裳,也可以把匿名函數(shù)賦值給一個變量逢防,再利用變量來調用該函數(shù).



裝飾器
顧名思義,裝飾函數(shù)使用的

由于函數(shù)也是一個對象贰健,而且函數(shù)對象可以被賦值給變量胞四,所以,通過變量也能調用該函數(shù)伶椿。

>>> def now():
...     print('2015-3-25')
...
>>> f = now       #  對于 now()函數(shù)辜伟,now()表示是函數(shù)的結果,即執(zhí)行了過程了脊另,而 now 只是函數(shù)的對象导狡,注意區(qū)分
>>> f()
2015-3-25

函數(shù)對象有一個__name__屬性,可以拿到函數(shù)的名字:

>>> now.__name__
'now'
>>> f.__name__
'now'

現(xiàn)在偎痛,假設我們要增強now()函數(shù)的功能旱捧,比如,在函數(shù)調用前后自動打印日志踩麦,但又不希望修改now()函數(shù)的定義枚赡,這種在代碼運行期間動態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)谓谦。

在面向對象(OOP)的設計模式中贫橙,decorator被稱為裝飾模式。OOP的裝飾模式需要通過繼承和組合來實現(xiàn)反粥,而Python除了能支持OOP的decorator外卢肃,直接從語法層次支持decorator疲迂。Python的decorator可以用函數(shù)實現(xiàn),也可以用類實現(xiàn)莫湘。


尤蒿。


續(xù)


decorator可以增強函數(shù)的功能,定義起來雖然有點復雜幅垮,但使用起來非常靈活和方便腰池。



偏函數(shù)
Python的functools模塊提供了很多有用的功能,其中一個就是偏函數(shù)(Partial function)忙芒。要注意巩螃,這里的偏函數(shù)和數(shù)學意義上的偏函數(shù)不一樣

nt()函數(shù)可以把字符串轉換為整數(shù),但int()函數(shù)還提供額外的base參數(shù),默認值為10匕争。如果傳入base參數(shù),就可以做N進制的轉換:

>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565

假設要轉換大量的二進制字符串爷耀,每次都傳入int(x, base=2)非常麻煩甘桑,于是,我們想到歹叮,可以定義一個int2()的函數(shù)跑杭,默認把base=2傳進去:

def int2(x, base=2):
    return int(x, base)

這樣,我們轉換二進制就非常方便了:

>>> int2('1000000')
64
>>> int2('1010101')
85

functools.partial就是幫助我們創(chuàng)建一個偏函數(shù)的咆耿,不需要我們自己定義int2()德谅,可以直接使用下面的代碼創(chuàng)建一個新的函數(shù)int2

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

所以,簡單總結functools.partial的作用就是萨螺,把一個函數(shù)的某些參數(shù)給固定渍觥(也就是設置默認值)雅采,返回一個新的函數(shù)丝蹭,調用這個新函數(shù)會更簡單赚瘦。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蹈垢,一起剝皮案震驚了整個濱河市沥寥,隨后出現(xiàn)的幾起案子薄霜,更是在濱河造成了極大的恐慌送膳,老刑警劉巖瘦癌,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艾帐,死亡現(xiàn)場離奇詭異乌叶,居然都是意外死亡,警方通過查閱死者的電腦和手機柒爸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門准浴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人揍鸟,你說我怎么就攤上這事兄裂【浜担” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵晰奖,是天一觀的道長谈撒。 經常有香客問我,道長匾南,這世上最難降的妖魔是什么啃匿? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮蛆楞,結果婚禮上溯乒,老公的妹妹穿的比我還像新娘。我一直安慰自己豹爹,他們只是感情好裆悄,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著臂聋,像睡著了一般光稼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上孩等,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天艾君,我揣著相機與錄音,去河邊找鬼肄方。 笑死冰垄,一個胖子當著我的面吹牛,可吹牛的內容都是我干的权她。 我是一名探鬼主播虹茶,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼隅要!你這毒婦竟也來了写烤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤拾徙,失蹤者是張志新(化名)和其女友劉穎洲炊,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尼啡,經...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡暂衡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了崖瞭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狂巢。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖书聚,靈堂內的尸體忽然破棺而出唧领,到底是詐尸還是另有隱情藻雌,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布斩个,位于F島的核電站胯杭,受9級特大地震影響,放射性物質發(fā)生泄漏受啥。R本人自食惡果不足惜做个,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望滚局。 院中可真熱鬧居暖,春花似錦、人聲如沸藤肢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘁圈。三九已至跟束,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丑孩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工灭贷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留温学,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓甚疟,卻偏偏與公主長得像仗岖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子览妖,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359