函數(shù)式編程

高階函數(shù)

變量可以指向函數(shù)

>>> abs(-1)
1
>>> f = abs
>>> f(-1)
1
>>> 
  • 變量f指向了abs函數(shù)本身。直接調(diào)用abs()函數(shù)和調(diào)用變量f()完全相同。

函數(shù)名也是變量

>>> abs = 1
>>> abs (-1)
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    abs (-1)
TypeError: 'int' object is not callable
>>> abs = f
>>> abs(-1)
1
>>> 
  • 函數(shù)名也只是一個變量,跟普通變量沒什么區(qū)別,只是他指向了一個函數(shù),其他變量指向整數(shù)或者字符串等等。
    *由于abs函數(shù)實際上是定義在import builtins模塊中的,所以要讓修改abs變量的指向在其它模塊也生效,要用import builtins; builtins.abs = 10。

傳入函數(shù)

  • 一個函數(shù)就可以接收另一個函數(shù)作為參數(shù),這種函數(shù)就稱之為高階函數(shù)。
def add(a, b, abs):
    return a + abs(b)

print(add(2, -3, abs))
==============================
5
  • 編寫高階函數(shù),就是讓函數(shù)的參數(shù)能夠接收別的函數(shù)。

map/reduce

map函數(shù)

  • map()函數(shù)接收兩個參數(shù),一個是函數(shù),一個是Iterable,map將傳入的函數(shù)依次作用到序列的每個元素,并把結(jié)果作為新的Iterator返回。
def f(a):
    return a*a

r=[1, 2, 3, 4, 5, 6, 7, 8, 9]
r = map(f, r)
print(list(r))
============================
[1, 4, 9, 16, 25, 36, 49, 64, 81]
  • map()傳入的第一個參數(shù)是f,即函數(shù)對象本身。由于結(jié)果r是一個Iterator,Iterator是惰性序列,因此通過list()函數(shù)讓它把整個序列都計算出來并返回一個list。

reduce函數(shù)

  • reduce把一個函數(shù)作用在一個序列[x1, x2, x3, ...]上,這個函數(shù)必須接收兩個參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個元素做累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
  • 如把序列[1, 2, 3, 4]變換成整數(shù)1234
import functools
def f(a, b):
    return a*10 + b

print(functools.reduce(f,[1,2,3,4]))
=============================
1234

filter

  • Python內(nèi)建的filter()函數(shù)用于過濾序列。
  • 和map()類似,filter()也接收一個函數(shù)和一個序列。和map()不同的是,filter()把傳入的函數(shù)依次作用于每個元素,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素。
  • 如保留一個List中的奇數(shù)
def is_odd(x):
    return x % 2 == 1


L1 = range(0, 5)
L2 = filter(is_odd, L1)
print(list(L2))
=========================
[1, 3]

sorted

  • Python內(nèi)置的sorted()函數(shù)就可以對list進行排序
  • sorted()函數(shù)也是一個高階函數(shù),它還可以接收一個key函數(shù)來實現(xiàn)自定義的排序,例如按絕對值大小排序:
L1 = [1, -2, 9, -3, 4]
L1 = sorted(L1, key=abs)
print(L1)
============================
[1, -2, -3, 4, 9]
  • 對字符串排序,是按照ASCII的大小比較的
  • 要進行反向排序,不必改動key函數(shù),可以傳入第三個參數(shù)reverse=True:
L2 = ["Jack", "Mike", "jason", "luke"]
L2 = sorted(L2, key=str.lower, reverse=True)
print(L2)
==============================
['Mike', 'luke', 'jason', 'Jack']

返回函數(shù)

  • 高階函數(shù)除了可以接受函數(shù)作為參數(shù)外,還可以把函數(shù)作為結(jié)果值返回。
def lazy_sum(*args):
    def cal_sum():
        ax = 0
        for n in args:
            ax = n + ax
        return ax
    return cal_sum


f1 = lazy_sum(1, 2, 3, 4, 5, 6, 7, 8)
f2 = lazy_sum(1, 2, 3, 4, 5, 6, 7, 8)
print(f1())
print(f1 == f2)
print(f1() == f2())

函數(shù)lazy_sum返回的是一個內(nèi)部定義的cal_sum函數(shù),所以只有調(diào)用f()才會顯示結(jié)果。
當我們調(diào)用lazy_sum()時,每次調(diào)用都會返回一個新的函數(shù),即使是傳入相同的參數(shù)
即f1()和f2()的調(diào)用結(jié)果互不影響。

閉包

  • 在上面的例子中,函數(shù)lazy_sum中又定義了函數(shù)cal_sum,內(nèi)部函數(shù)cal_sum可以引用外部函數(shù)lazy_sum的參數(shù)和局部變量,當lazy_sum返回函數(shù)cal_sum時,相關(guān)參數(shù)和變量都保存在返回的函數(shù)中,這種稱為“閉包(Closure)”的程序結(jié)構(gòu)擁有極大的威力。
  • 當一個函數(shù)返回了一個函數(shù)后,其內(nèi)部的局部變量還被新函數(shù)引用
  • 返回的函數(shù)并沒有立刻執(zhí)行,而是直到調(diào)用了f()才執(zhí)行
def count():
    fs = []
    for i in range(1, 4):
        def f():
            return i*i
        fs.append(f)
    return fs


f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())
=================================
9
9
9

可以看到上面的輸出全為9,而不是1,4,9,是因為在調(diào)用f1(),f2(),f3()時,i已經(jīng)變成了3

  • 返回閉包時牢記的一點就是:返回函數(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))
    return fs
=========================
1
4
9

匿名函數(shù)

  • 當我們在傳入函數(shù)時,有些時候,不需要顯式地定義函數(shù),直接傳入匿名函數(shù)更方便。
L = list(map(lambda x: x*x, [1, 2, 3, 4, 5]))
print(L)
===================================
[1, 4, 9, 16, 25]
  • 用匿名函數(shù)有個好處,因為函數(shù)沒有名字,不必擔(dān)心函數(shù)名沖突。此外,匿名函數(shù)也是一個函數(shù)對象,也可以把匿名函數(shù)賦值給一個變量,再利用變量來調(diào)用該函數(shù)
f = lambda x: x*x
print(f(5))
======================
25
  • 也可以把匿名函數(shù)作為返回值返回
def get_sum(x, y):
    return lambda: x+y

f = get_sum(1,2)
print(f())

裝飾器

  • decorator就是一個返回函數(shù)的高階函數(shù)。
  • 定義一個能打印日志的decorator:
def log(func):
    def wrapper(*args, **kw):
        print('call %s()' % func.__name__)
        return func(*args, **kw)
    return wrapper
  • 借助Python的@語法,把decorator置于函數(shù)的定義
@log()
def now():
    print(time.strftime("%y %m %d", time.localtime()))
  • 調(diào)用now()函數(shù),不僅會運行now()函數(shù)本身,還會在運行now()函數(shù)前打印一行日志:
call now()
17 11 07
  • 相當執(zhí)行了:
now = log(now)
  • 可想而知,now的函數(shù)名發(fā)生了變化:
>>> now.__name__
'wrapper'
  • 使用Python內(nèi)置的functools.wraps解決:
def log(*text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print("begin call")
            if text:
                print('%s %s' % (text[0], func.__name__))
            else:
                print('call %s()' % func.__name__)
            func(*args, **kw)
            print("end call")
            return
        return wrapper
    return decorator

該函數(shù)經(jīng)過了一些修改

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

偏函數(shù)

  • 偏函數(shù)是指通過設(shè)定參數(shù)的默認值,可以降低函數(shù)調(diào)用的難度。
  • functools.partial可以幫助我們創(chuàng)建一個偏函數(shù)
import functools
int2 = functools.partial(int, base=2)
print(int2('100000'))
  • 新的int2函數(shù),僅僅是把base參數(shù)重新設(shè)定默認值為2,但也可以在函數(shù)調(diào)用時傳入其他值
print(int2('100000', base = 8))
  • 創(chuàng)建偏函數(shù)時,實際上可以接收函數(shù)對象、args和*kw這3個參數(shù),當傳入:
int2 = functools.partial(int, base=2)

相當于:

kw = { 'base': 2 }
int('10010', **kw)
``
當傳入:

max2 = functools.partial(max, 10)

實際上會把10作為*args的一部分自動加到左邊
相當于:

args = (10, 5, 6, 7)
max(*args)






最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內(nèi)容