負基礎小白學習廖雪峰python教程記錄(四)函數(shù)式編程

高階函數(shù)

前言

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

>>> f = abs

>>> f(-10)

10

函數(shù)名也是變量

>>> abs = 10

>>> abs(-10)

Traceback (most recent call last):?

?File"<stdin>", line1, in <module>

Type Error:'int' object is not callable


傳入函數(shù)

一個函數(shù)就可以接收另一個函數(shù)作為參數(shù),這種函數(shù)就稱之為高階函數(shù)

def add(x, y, f):

????return f(x) + f(y)


map/reduce

map

map()函數(shù)接收兩個參數(shù),一個是函數(shù),一個是Iterable,map將傳入的函數(shù)依次作用到序列的每個元素,并把結果作為新的Iterator返回

>>> def f(x):

...? ? 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]


>>> list(map(str,[1,2,3,4,5,6,7,8,9]))

['1', '2', '3', '4', '5', '6', '7', '8', '9']


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

>>> def add(x,y):

...? ? return x+y

...

>>> reduce(add,[1,3,5,7,9])

25


把序列[1, 3, 5, 7, 9]變換成整數(shù)13579,reduce用法:

>>> from functools import reduce

>>> def fn(x,y):

...? ? return x*10+y

...

>>> reduce(fn,[1,3,5,7,9])

13579


練習

第一題:
>>> def normalize(name):

...? ? a= name[0].upper()

...? ? b= name[1:].lower()

...? ? name=a+b

...? ? return name

...

>>> L1 = ['adam', 'LISA', 'barT']

>>> L2 = list(map(normalize, L1))

>>> print(L2)

['Adam', 'Lisa', 'Bart']


第二題:

>>> def prod(x,y):

...? ? return x*y

...

>>> reduce(prod,[3,5,7,9])


第三題:

?


filter

filter()函數(shù)用于過濾序列

例如,在一個list中,刪掉偶數(shù),只保留奇數(shù):

>>> def is_odd(n):

...? ? return n%2==1

...

>>> list(filter(is_odd,[1,2,4,5,6,9,10,15]))

[1, 5, 9, 15]

用filter求素數(shù)

計算素數(shù)的一個方法是埃氏篩法。

首先,列出從2開始的所有自然數(shù),構造一個序列。

取序列的第一個數(shù)2,它一定是素數(shù),然后用2把序列的2的倍數(shù)篩掉

取新序列的第一個數(shù)3,它一定是素數(shù),然后用3把序列的3的倍數(shù)篩掉

取新序列的第一個數(shù)5,然后用5把序列的5的倍數(shù)篩掉

不斷篩下去,就可以得到所有的素數(shù)。

用Python來實現(xiàn)這個算法,可以先構造一個從3開始的奇數(shù)序列:

def_odd_iter():

????n =1

????while True:?

? ? ? ? n = n +2

????????yield n

然后定義一個篩選函數(shù):

def_not_divisible(n):

????return lambda x: x % n >0

最后,定義一個生成器,不斷返回下一個素數(shù):

def primes():

????yield 2?

????it = _odd_iter()? ? # 初始序列

? ? whileTrue:?

? ? ? ? n = next(it)? ? ? # 返回序列的第一個數(shù)

????????yield n?

? ? ? ? it = filter(_not_divisible(n), it)? ? # 構造新序列

這個生成器先返回第一個素數(shù)2,然后,利用filter()不斷產生篩選后的新的序列。由于primes()也是一個無限序列,所以調用時需要設置一個退出循環(huán)的條件:

# 打印1000以內的素數(shù):

for n in primes():

????if n <1000:?

? ? ? ? print(n)

????else:

????????break


練習

回數(shù)是指從左向右讀和從右向左讀都是一樣的數(shù),例如12321,909。請利用filter()篩選出回數(shù):

def?is_palindrome(n):

????return?str(n)?==?str(n)[::-1]


sorted

sorted()也是一個高階函數(shù)。用sorted()排序的關鍵在于實現(xiàn)一個映射函數(shù)。

>>> sorted([36,5,-12,9,-21])

[-21, -12, 5, 9, 36]

絕對值排序:

>>> sorted([36,5,-12,9,-21],key=abs)

[5, 9, -12, -21, 36]

字符串排序,按照ASCII的大小比較的,由于'Z' < 'a',結果,大寫字母Z會排在小寫字母a的前面:

>>> sorted(['bob','about','Zoo','Credit'])

['Credit', 'Zoo', 'about', 'bob']

忽略大小寫的排序:

>>> sorted(['bob','about','Zoo','Credit'], key=str.lower)

['about','bob','Credit','Zoo']

反向排序:

>>> sorted(['bob','about','Zoo','Credit'], key=str.lower,reverse=True)

['Zoo', 'Credit', 'bob', 'about']


練習

L是用tuple來表示一對名字和分數(shù)的。

key是用來進行比較的元素,只有一個參數(shù),具體的函數(shù)的參數(shù)就是取自于可迭代對象中,指定可迭代對象中的一個元素來進行排序。

具體的函數(shù)的參數(shù)就是取自于可迭代對象中,指定可迭代對象中的一個元素來進行排序。

那么我們看代碼中的t表示啥,表示的是不是可迭代對象(list)中的一個元素(tuple類型)

一個tuple的表示形式是('名字':分數(shù))?? ('Bob': 75)

?那么t[0]和t[1],前一個名字,后一個分數(shù)。轉換到key中去就是按照名字排序,和按照分數(shù)排序。

>>> L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]

>>> def by_name(t):

...? ? ? return t[0]

...

>>> sorted(L, key=by_name)


>>> def by_score(t):

...? ? return -t[1]? ? ? ?#成績降序排列

...

>>> sorted(L, key=by_score)

[('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]


返回函數(shù)

函數(shù)作為返回值

通常情況下的求和:

def calc_sum(*args):

????ax =0

????for n in args:?

????????ax = ax + n

????return ax

需要再計算的話,可以不返回求和的結果,而是返回求和的函數(shù):

def lazy_sum(*args):

????def sum():

????????ax =0

????????for n in args:?

????????????ax = ax + n

????????????return ax

????return sum

在函數(shù)lazy_sum中又定義了函數(shù)sum,并且,內部函數(shù)sum可以引用外部函數(shù)lazy_sum的參數(shù)和局部變量,當lazy_sum返回函數(shù)sum時,相關參數(shù)和變量都保存在返回的函數(shù)中。(閉包程序)


閉包

返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會發(fā)生變化的變量!?。。?/b>

>>> def count():

...? ? fs=[]

...? ? for i in range(1,4):

...? ? ? ? ? ? def f():

...? ? ? ? ? ? ? ? ? ? return i*i

...? ? ? ? ? ? fs.append(f)

...? ? return fs

...

>>> f1,f2,f3=count()

>>> f1()

9

>>> f2()

9

>>> f3()

9

輸出結果不是1,4,9。原因就在于返回的函數(shù)引用了變量i,但它并非立刻執(zhí)行。等到3個函數(shù)都返回時,它們所引用的變量i已經變成了3,因此最終結果為9。

如果一定要引用循環(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

...

>>> f1, f2, f3=count()

>>> f1()

1

>>> f2()

4

>>> f3()

9


作業(yè)

暫缺



匿名函數(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ù):

>>> f=lambda x: x*x

>>> f

<function <lambda> at 0x000001CE0FDCEE50>

>>> f(6)

36

也可以把匿名函數(shù)作為返回值返回,比如:

def build(x, y):

????return lambda: x * x + y * y


作業(yè)

用匿名函數(shù)改寫:

def is_odd(n):

? ? return n % 2 == 1

L = list(filter(is_odd, range(1, 20)))

改寫后:

L=list(filter(lambda n: n%2==1, range(1, 20)))

print(L)


裝飾器

函數(shù)對象可以被賦值給變量,所以,通過變量也能調用該函數(shù)。

>>> def now():

...? ? print('2015-3-25')

...

>>> f=now

>>> f()

2015-3-25

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

>>> now.__name__? ? ? ? #兩個下劃線name兩個下劃線

'now'

>>> f.__name__

'now'

在代碼運行期間動態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)

>>> def log(func):

...? ? def wrapper(*arg,**kw):

...? ? ? ? ? ? print('call %s():' % func.__name__)

...? ? ? ? ? ? return func(*arg,**kw)

...? ? return wrapper

借助Python的@語法,把decorator置于函數(shù)的定義處

>>> @log

... def now():

...? ? print('2015-3-25')

相當于執(zhí)行了語句:now =log(now)

調用now()函數(shù),不僅會運行now()函數(shù)本身,還會在運行now()函數(shù)前打印一行日志:

>>> now()

call now():

2015-3-25


>>> def log(text):

...? ? def decorator(func):

...? ? ? ? ? ? def wrapper(*args,**kw):

...? ? ? ? ? ? ? ? ? ? print('%s %s():' % (text, func.__name__))

...? ? ? ? ? ? ? ? ? ? return func(*args,**kw)

...? ? ? ? ? ? return wrapper

...? ? return decorator

這個3層嵌套的decorator用法如下:

>>> @log('execute')

... def now():

...? ? print('2015-3-25')

執(zhí)行結果如下:

>>> now()

execute now():

2015-3-25

此時:

>>> now.__name__

'wrapper'

import functools

>>> import functools

>>> def log(func):

...? ? @functools.wraps(func)

...? ? def wrapper(*args, **kw):

...? ? ? ? ? ? print('claa %s():' % func.__name__)

...? ? ? ? ? ? return func(*args,**kw)

...? ? return wrapper

或者針對帶參數(shù)的decorator:

>>> import functools

>>> def log(text):

...? ? def decorator(func):

...? ? ? ? ? ? @functools.wrap(func)

...? ? ? ? ? ? def wrapper(*args, **kw):

...? ? ? ? ? ? ? ? ? ? print('%s %s():' % (text, func.__name__))

...? ? ? ? ? ? ? ? ? ? return func(*args, **kw)

...? ? ? ? ? ? return wrapper

...? ? return decorator

作業(yè)

暫缺


偏函數(shù)

把一個函數(shù)的某些參數(shù)給固定?。ㄒ簿褪窃O置默認值),返回一個新的函數(shù),調用這個新函數(shù)會更簡單。

通過設定參數(shù)的默認值,可以降低函數(shù)調用的難度。而偏函數(shù)也可以做到這一點。f

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

>>> import functools

>>> int2=functools.partial(int, base=2)

>>> int('1000000')

1000000

>>> int2('1000000')

64

僅僅是把base參數(shù)重新設定默認值為2,但也可以在函數(shù)調用時傳入其他值:

>>> int2('1000000', base=10)

1000000


創(chuàng)建偏函數(shù)時,實際上可以接收函數(shù)對象、*args和**kw這3個參數(shù),當傳入:

int2 = functools.partial (int, base=2)

實際上固定了int()函數(shù)的關鍵字參數(shù)base,也就是:

int2('10010')

相當于:

kw = {'base':2}

int('10010', **kw)

傳入:

max2 =functools.partial(max,10)

實際上會把10作為*args的一部分自動加到左邊,也就是:

max2(5, 6, 7)

args = (10, 5, 6, 7)

max(*args)

結果為10。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容