1. 高階函數(shù)
函數(shù)本身可以做為變量賦值,如
f = abs
<built-in function abs>
這時(shí)候調(diào)用被賦值的函數(shù)變量,與函數(shù)的調(diào)用結(jié)果是相同的
num = f(-10)
>>> 10
函數(shù)名本身是一個(gè)變量,且可以作為另一個(gè)函數(shù)的參數(shù)
def add(x, y, f):
return f(x) + f(y)
num = add(-5, 8, abs)
講abs函數(shù)作為參數(shù)傳入另一個(gè)函數(shù)中。
學(xué)習(xí)了兩個(gè)內(nèi)建函數(shù)
- map() 函數(shù)
map函數(shù)表示將函數(shù)作用于列表的每一個(gè)元素上
def f(x)
return x *x
r = map(f, list(range(1, 10)))
r = map(str, list(range(1,10)))
注意:生成的是一個(gè)新的惰性序列,當(dāng)調(diào)用next時(shí)才計(jì)算出下一個(gè)元素
- reduce 函數(shù)
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
from functools import reduce
def reAdd(x, y):
return x + y
r = reduce(reAdd, list(range(1, 10)))
計(jì)算序列的累加
注:reduce 已經(jīng)被移至functools
題:把str轉(zhuǎn)換為int的函數(shù)
def fn(x, y):
return x * 10 + y
def char2num(s):
return ord(s) - ord('0')
reduce(fn, map(char2num, '13579'))
>>> 13578
練習(xí):
利用map()函數(shù),把用戶輸入的不規(guī)范的英文名字,變?yōu)槭鬃帜复髮?xiě),其他小寫(xiě)的規(guī)范名字。輸入:['adam', 'LISA', 'barT'],輸出:['Adam', 'Lisa', 'Bart']:
def normalize(name):
L1, L2 = [], []
if len(name) > 0:
L1 = name[:1]
if len(name) > 1:
L2 = name[1:]
L1 = L1.upper()
L2 = L2.lower()
return L1 + L2
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
['Adam', 'Lisa', 'Bart']
練習(xí):
Python提供的sum()函數(shù)可以接受一個(gè)list并求和,請(qǐng)編寫(xiě)一個(gè)prod()函數(shù),可以接受一個(gè)list并利用reduce()求積:
def prod(L):
return reduce(lambda x, y : x * y, L)
>>> prod([3, 5, 7, 9])
945
練習(xí):
利用map和reduce編寫(xiě)一個(gè)str2float函數(shù),把字符串'123.456'轉(zhuǎn)換成浮點(diǎn)數(shù)123.456:
def str2float(s):
if len(s) > 0:
i = 0
L1 = s[:s.find('.')]
L2 = s[:s.find('.'):-1] # 用這種方式取逆序,list。find() 尋找元素下標(biāo)
# L2 = list(s[s.find('.') + 1:])
# L2.reverse()
def fn2ten(x, y):
return x * 10 + y
def fn2one(x, y):
return x * 0.1 + y
else:
return 0
return reduce(fn2ten, map(lambda x: ord(x) - ord('0'), L1)) + reduce(fn2one, map(lambda x: ord(x) - ord('0'), L2)) / 10 # ord()函數(shù),取字符串對(duì)應(yīng)阿斯克碼
- filter() 函數(shù)
傳入返回值為布爾值的函數(shù),用于過(guò)濾列表
def is_odd(n):
return n % 2 == 0
print(list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])))
[2, 4, 6, 8, 10]
注意到filter()函數(shù)返回的是一個(gè)Iterator,也就是一個(gè)惰性序列
用filter求素?cái)?shù)
計(jì)算素?cái)?shù)的一個(gè)方法是埃氏篩法,它的算法理解起來(lái)非常簡(jiǎn)單:
- 首先,列出從2開(kāi)始的所有自然數(shù),構(gòu)造一個(gè)序列
- 取序列的第一個(gè)數(shù)2,它一定是素?cái)?shù),然后用2把序列的2的倍數(shù)篩掉
- 取新序列的第一個(gè)數(shù)3,它一定是素?cái)?shù),然后用3把序列的3的倍數(shù)篩掉
...- 不斷篩下去,就可以得到所有的素?cái)?shù)
# 構(gòu)造一個(gè)從3開(kāi)始的奇數(shù)序列(剔除2的倍數(shù),直接從第三步開(kāi)始)
def _odd_iter():
n = 1
while True:
n += 2
yield n
# 然后定義一個(gè)篩選函數(shù):
def _not_divisible(n):
return lambda x: x % n > 0
# 最后,定義一個(gè)生成器,不斷返回下一個(gè)素?cái)?shù):
def primes():
yield 2
it = _odd_iter() # 生成3開(kāi)始的奇數(shù)序列
while True:
n = next(it) # 得到3
yield n # 返回3
it = filter(_not_divisible(n), it) # 篩除3的倍數(shù),然后生成一個(gè)從5開(kāi)始的序列...
練習(xí)
回?cái)?shù)是指從左向右讀和從右向左讀都是一樣的數(shù),例如12321,909。請(qǐng)利用filter()篩選出回?cái)?shù):
def _all_nums():
n = 0
while True:
yield n
n += 1
def num2char(n):
return str(n)
def is_palindrome(n):
s = num2char(n)
mid = len(s) // 2 - 1
n = 0
while n <= mid:
if s[n] == s[-n - 1]:
n += 1
else:
return False
return True
filter(is_palindrome, range(1, 1000)) # 1~1000的回文
小結(jié)
filter()的作用是從一個(gè)序列中篩出符合條件的元素。由于filter()使用了惰性計(jì)算,所以只有在取filter()結(jié)果的時(shí)候,才會(huì)真正篩選并每次返回下一個(gè)篩出的元素。
- sorted()函數(shù)
語(yǔ)法:sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
sorted(list, options, reverse) 1.列表 2.對(duì)元素的操作,返回排序元素 3.是否逆序輸出
sorted()也是一個(gè)高階函數(shù)。用sorted()排序的關(guān)鍵在于實(shí)現(xiàn)一個(gè)映射函數(shù)。
練習(xí)
假設(shè)我們用一組tuple表示學(xué)生名字和成績(jī):
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
按姓名排序
def by_name(t):
t2 = t[0].lower()
return t2 # 返回要排序的部分
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
L2 = sorted(L, key=by_name)
再按成績(jī)從高到低排序
def by_score(t):
t2 = t[1]
return t2
L2 = sorted(L, key=by_score, reverse=True) # 從高到低需要逆序輸出
2. 返回函數(shù)
函數(shù)作為返回值
- 閉包
!!! 返回閉包時(shí)牢記一點(diǎn):返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量。
練習(xí)
利用閉包返回一個(gè)計(jì)數(shù)器函數(shù),每次調(diào)用它返回遞增整數(shù)
def createCounter():
global n # 定義一個(gè)全局變量
n = 0
def counter():
global n
n += 1
return n
return counter
3. 匿名函數(shù)
匿名函數(shù)lambda x: x * x實(shí)際上就是:
def f(x):
return x * x
關(guān)鍵字lambda表示匿名函數(shù),冒號(hào)前面的x表示函數(shù)參數(shù),匿名函數(shù)有個(gè)限制,就是只能有一個(gè)表達(dá)式,不用寫(xiě)return,返回值就是該表達(dá)式的結(jié)果
練習(xí)
請(qǐng)用匿名函數(shù)改造下面的代碼:
def is_odd(n):
return n % 2 == 1
L = list(filter(is_odd, range(1, 20)))
# 改造后
L = list(filter(lambda x: x % 2 == 1, range(1, 20)))
4. 裝飾器
在代碼運(yùn)行期間動(dòng)態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)
練習(xí)
請(qǐng)?jiān)O(shè)計(jì)一個(gè)decorator,它可作用于任何函數(shù)上,并打印該函數(shù)的執(zhí)行時(shí)間
import functools, time
def metric(func):
@functools.wraps(func) # 不改變?cè)瘮?shù)的名稱
def wrapper(*args, **kw):
print('%s executed in %s ms' % (func.__name__, time.ctime()))
f = func(*args, **kw)
return f
return wrapper
使用方法,在函數(shù)的定義時(shí)加上裝飾器函數(shù),用@
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y
練習(xí)
請(qǐng)編寫(xiě)一個(gè)decorator,能在函數(shù)調(diào)用的前后打印出'begin call'和'end call'的日志。支持:傳參和不傳參
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
if isinstance(text, str):
print(text)
print('begin call')
func(*args, **kw)
print('end call')
return wrapper
if isinstance(text, str):
return decorator
else:
return decorator(text)
5. 偏函數(shù)
當(dāng)函數(shù)的參數(shù)個(gè)數(shù)太多,需要簡(jiǎn)化時(shí),使用functools.partial可以創(chuàng)建一個(gè)新的函數(shù),這個(gè)新函數(shù)可以固定住原函數(shù)的部分參數(shù),從而在調(diào)用時(shí)更簡(jiǎn)單。
比如,將int函數(shù)改為接受二進(jìn)制源
# 原本
int('10010', 2)
# 修改
int2 = functools.partial(int, base = 2)
int2('10010')
>>> int('10010', 2)
18
>>> int2('10010')
18