python 函數(shù)式編程

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)單:

  1. 首先,列出從2開(kāi)始的所有自然數(shù),構(gòu)造一個(gè)序列
  2. 取序列的第一個(gè)數(shù)2,它一定是素?cái)?shù),然后用2把序列的2的倍數(shù)篩掉
  3. 取新序列的第一個(gè)數(shù)3,它一定是素?cái)?shù),然后用3把序列的3的倍數(shù)篩掉
    ...
  4. 不斷篩下去,就可以得到所有的素?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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 函數(shù)是Python內(nèi)建支持的一種封裝,我們通過(guò)把大段代碼拆成函數(shù),通過(guò)一層一層的函數(shù)調(diào)用,就可以把復(fù)雜任務(wù)分解成簡(jiǎn)...
    祐吢房_2c9a閱讀 464評(píng)論 1 1
  • 函數(shù)式編程就是一種抽象程度很高的編程范式,純粹的函數(shù)式編程語(yǔ)言編寫(xiě)的函數(shù)沒(méi)有變量,因此,任意一個(gè)函數(shù),只要輸入是確...
    齊天大圣李圣杰閱讀 1,632評(píng)論 0 2
  • 本系列主要學(xué)習(xí)Python的基本使用和語(yǔ)法知識(shí),后續(xù)可能會(huì)圍繞著AI學(xué)習(xí)展開(kāi)。Python3 (1) Python...
    猿來(lái)如癡閱讀 2,449評(píng)論 0 0
  • 函數(shù)式編程就是一種抽象程度很高的編程范式,純粹的函數(shù)式編程語(yǔ)言編寫(xiě)的函數(shù)沒(méi)有變量,因此,任意一個(gè)函數(shù),只要輸入是確...
    DramaScript閱讀 319評(píng)論 0 0
  • 高階python 函數(shù)式編程 - 函數(shù)式 函數(shù)式編程(FunctionalProgramming) - 基于lam...
    July_whj閱讀 576評(píng)論 0 1

友情鏈接更多精彩內(nèi)容