欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python技巧匿名函數(shù)、回調(diào)函數(shù)和高階函數(shù)

 更新時(shí)間:2021年12月03日 09:36:50   作者:Orion's Blog  
本文分享的是Python技巧匿名函數(shù)、回調(diào)函數(shù)和高階函數(shù),我們?cè)赑ython中使用lambda表達(dá)式來使用匿名函數(shù),回調(diào)函數(shù)即callback,先寫一個(gè)函數(shù),讓預(yù)先寫好的系統(tǒng)來調(diào)用,一個(gè)函數(shù)可以作為參數(shù)傳給另外一個(gè)函數(shù),或者一個(gè)函數(shù)的返回值為另外一個(gè)函數(shù),滿足其一則為高階函數(shù)

1、定義匿名或內(nèi)聯(lián)函數(shù)

如果我們想提供一個(gè)短小的回調(diào)函數(shù)供sort()這樣的函數(shù)用,但不想用def這樣的語(yǔ)句編寫一個(gè)單行的函數(shù),我們可以借助lambda表達(dá)式來編寫“內(nèi)聯(lián)”式的函數(shù)。

如下圖所示:

add = lambda x, y: x + y
print(add(2, 3)) # 5
print(add("hello", "world!")) # helloworld


可以看到,這里用到的lambda表達(dá)式和普通的函數(shù)定義有著相同的功能。
lambda表達(dá)式常常做為回調(diào)函數(shù)使用,有在排序以及對(duì)數(shù)據(jù)進(jìn)行預(yù)處理時(shí)有許多用武之地,

如下所示:

names = [ 'David Beazley', 'Brian Jones', 'Reymond Hettinger', 'Ned Batchelder']
sorted_names = sorted(names, key=lambda name: name.split()[-1].lower())
print(sorted_names)
# ['Ned Batchelder', 'David Beazley', 'Reymond Hettinger', 'Brian Jones']


lambda雖然靈活易用,但是局限性也大,相當(dāng)于其函數(shù)體中只能定義一條語(yǔ)句,不能執(zhí)行條件分支、迭代、異常處理等操作。

2、在匿名函數(shù)中綁定變量的值

現(xiàn)在我們想在匿名函數(shù)定義時(shí)完成對(duì)特定變量(一般是常量)的綁定,以便后期使用。

如果我們這樣寫:

x = 10
a = lambda y: x + y 
x = 20
b = lambda y: x + y


然后計(jì)算a(10)和b(10)。你可能希望結(jié)果是20和30,然而實(shí)際程序的運(yùn)行結(jié)果會(huì)出人意料:結(jié)果是30和30。
這個(gè)問題的關(guān)鍵在于lambda表達(dá)式中的x是個(gè)自由變量(未綁定到本地作用域的變量),在運(yùn)行時(shí)綁定而不是定義的時(shí)候綁定(其實(shí)普通函數(shù)中使用自由變量同理),而這里執(zhí)行a(10)的時(shí)候x已經(jīng)變成了20,故最終a(10)的值為30。如果希望匿名函數(shù)在定義的時(shí)候綁定變量,而之后綁定值不再變化,那我們可以將想要綁定的變量做為默認(rèn)參數(shù),

如下所示:

x = 10
a = lambda y, x=x: x + y
x = 20
b = lambda y, x=x: x + y
print(a(10)) # 20
print(b(10)) # 30


上面我們提到的這個(gè)陷阱常見于一些對(duì)lambda函數(shù)過于“聰明”的應(yīng)用中。比如我們想用列表推導(dǎo)式來創(chuàng)建一個(gè)列表的lambda函數(shù)并期望lambda函數(shù)能記住迭代變量。

funcs = [lambda x: x + n for n in range(5)]
for f in funcs:
    print(f(0))
# 4
# 4
# 4
# 4
# 4


可以看到與我們期望的不同,所有lambda函數(shù)都認(rèn)為n是4。

如上所述,我們修改成以下代碼即可:

funcs = [lambda x, n=n: x + n for n in range(5)]
for f in funcs:
    print(f(0))
# 0
# 1
# 2
# 3
# 4


3、讓帶有n個(gè)參數(shù)的可調(diào)用對(duì)象以較少的參數(shù)調(diào)用

假設(shè)我們現(xiàn)在有個(gè)n個(gè)參數(shù)的函數(shù)做為回調(diào)函數(shù)使用,但這個(gè)函數(shù)需要的參數(shù)過多,而回調(diào)函數(shù)只能有個(gè)參數(shù)。如果需要減少函數(shù)的參數(shù)數(shù)量,需要時(shí)用functools包。functools這個(gè)包內(nèi)的函數(shù)全部為高階函數(shù)。高階函數(shù)即參數(shù)或(和)返回值為其他函數(shù)的函數(shù)。通常來說,此模塊的功能適用于所有可調(diào)用對(duì)象。

比如functools.partial()就是一個(gè)高階函數(shù), 它的原型如下:

functools.partial(func, /, *args, **keywords)


它接受一個(gè)func函數(shù)做為參數(shù),并且它會(huì)返回一個(gè)新的newfunc對(duì)象,這個(gè)新的newfunc對(duì)象已經(jīng)附帶了位置參數(shù)args和關(guān)鍵字參數(shù)keywords,之后在調(diào)用newfunc時(shí)就可以不用再傳已經(jīng)設(shè)定好的參數(shù)了。

如下所示:

def spam(a, b, c, d):
  print(a, b, c, d)

from functools import partial
s1 = partial(spam, 1) # 設(shè)定好a = 1(如果沒指定參數(shù)名,默認(rèn)按順序設(shè)定)
s1(2, 3, 4) # 1 2 3 4

s2 = partial(spam, d=42) # 設(shè)定好d為42
s2(1, 2, 3) # 1 2 3 42

s3 = partial(spam, 1, 2, d=42) #設(shè)定好a = 1, b = 2, d = 42
s3(3) # 1 2 3 42

上面提到的技術(shù)常常用于將不兼容的代碼“粘”起來,尤其是在你調(diào)用別人的輪子,而別人寫好的函數(shù)不能修改的時(shí)候。比如我們有以下一組元組表示的點(diǎn)的坐標(biāo):

points = [(1, 2), (3, 4), (5, 6), (7, 8)]


有已知的一個(gè)distance()函數(shù)可供使用,假設(shè)這是別人造的輪子不能修改。

import math
def distance(p1, p2):
    x1, y1 = p1
    x2, y2 = p2
    return math.hypot(x2 - x1, y2 - y1)


接下來我們想根據(jù)列表中這些點(diǎn)到一個(gè)定點(diǎn)pt=(4, 3)的距離來排序。我們知道列表的sort()方法
可以接受一個(gè)key參數(shù)(傳入一個(gè)回調(diào)函數(shù))來做自定義的排序處理。但傳入的回調(diào)函數(shù)只能有一個(gè)參數(shù),這里的distance()函數(shù)有兩個(gè)參數(shù),顯然不能直接做為回調(diào)函數(shù)使用。

下面我們用partical()來解決這個(gè)問題:

pt = (4, 3)
points.sort(key=partial(distance, pt)) # 先指定好一個(gè)參數(shù)為pt=(4,3)
print(points)
# [(3, 4), (1, 2), (5, 6), (7, 8)]


可以看到,排序正確運(yùn)行。還有一種方法要臃腫些,那就是將回調(diào)函數(shù)distance嵌套進(jìn)另一個(gè)只有一個(gè)參數(shù)的lambda函數(shù)中:

pt = (4, 3)
points.sort(key=lambda p: distance(p, pt))
print(points)
# [(3, 4), (1, 2), (5, 6), (7, 8)]


這種方法一來臃腫,二來仍然存在我們上面提到過的一個(gè)毛病,如果我們定義回調(diào)函數(shù)后對(duì)pt有所修改,就會(huì)發(fā)生我們上面所說的不愉快的事情:

pt = (4, 3)
func_key = lambda p: distance(p ,pt) 
pt = (0, 0) # 像這樣,后面pt變了就GG
points.sort(key=func_key)
print(points)
# [(1, 2), (3, 4), (5, 6), (7, 8)]


可以看到,最終排序的結(jié)果由于后面pt的改變而變得完全不同了。所以我們還是建議大家采用使用functools.partial()函數(shù)來達(dá)成目的。
下面這段代碼也是用partial()函數(shù)來調(diào)整函數(shù)簽名的例子。這段代碼利用multiprocessing模塊以異步方式計(jì)算某個(gè)結(jié)果,然后用一個(gè)回調(diào)函數(shù)來打印該結(jié)果,該回調(diào)函數(shù)可接受這個(gè)結(jié)果和一個(gè)事先指定好的日志參數(shù)。

# result:回調(diào)函數(shù)本身該接受的參數(shù), log是我想使其擴(kuò)展的參數(shù)
def output_result(result, log=None):
    if log is not None:
        log.debug('Got: %r', result)

def add(x, y):
    return x + y

if __name__ == '__main__':
    import logging
    from multiprocessing import Pool
    from functools import partial
    logging.basicConfig(level=logging.DEBUG)
    log = logging.getLogger('test')
    p = Pool()
    p.apply_async(add, (3, 4), callback=partial(output_result, log=log))
    p.close()
    p.join()

# DEBUG:test:Got: 7

下面這個(gè)例子則源于一個(gè)在編寫網(wǎng)絡(luò)服務(wù)器中所面對(duì)的問題。比如我們?cè)?code>socketServer模塊的基礎(chǔ)上,

編寫了下面這個(gè)簡(jiǎn)單的echo服務(wù)程序:

from socketserver import StreamRequestHandler, TCPServer
class EchoHandler(StreamRequestHandler):
    def handle(self):
        for line in self.rfile:
            self.wfile.write(b'GoT:' + line)

serv = TCPServer(('', 15000), EchoHandler)
serv.serve_forever()

現(xiàn)在,我們想在EchoHandler類中增加一個(gè)__init__()方法,它接受額外的一個(gè)配置參數(shù),用于事先指定ack。即:

class EchoHandler(StreamRequestHandler):
    def __init__(self, *args, ack, **kwargs):
        self.ack = ack
        super().__init__(*args, **kwargs) 
    def handle(self) -> None:
        for line in self.rfile:
            self.wfile.write(self.ack + line)


假如我們就這樣直接改動(dòng),就會(huì)發(fā)現(xiàn)后面會(huì)提示__init__()函數(shù)缺少keyword-only參數(shù)ack(這里調(diào)用EchoHandler()初始化對(duì)象的時(shí)候會(huì)隱式調(diào)用__init__()函數(shù))。 我們用partical()也能輕松解決這個(gè)問題,即為EchoHandler()事先提供好ack參數(shù)。

from functools import partial
serv = TCPServer(('', 15000), partial(EchoHandler, ack=b'RECEIVED'))
serv.serve_forever()


4、在回調(diào)函數(shù)中攜帶額外的狀態(tài)

我們知道,我們調(diào)用回調(diào)函數(shù)后,就會(huì)跳轉(zhuǎn)到一個(gè)全新的環(huán)境,此時(shí)會(huì)丟失我們?cè)镜沫h(huán)境狀態(tài)。接下來我們討論如何在回調(diào)函數(shù)中攜帶額外的狀態(tài)以便在回調(diào)函數(shù)內(nèi)部使用。
因?yàn)閷?duì)回調(diào)函數(shù)的應(yīng)用在與異步處理相關(guān)的庫(kù)和框架中比較常見,我們下面的例子也多和異步處理相關(guān)。現(xiàn)在我們定義了一個(gè)異步處理函數(shù),它會(huì)調(diào)用一個(gè)回調(diào)函數(shù)。

def apply_async(func, args, *, callback):
    # 計(jì)算結(jié)果
    result = func(*args)
    # 將結(jié)果傳給回調(diào)函數(shù)
    callback(result)


下面展示上述代碼如何使用:

# 要回調(diào)的函數(shù)
def print_result(result):
    print("Got: ", result)
    
def add(x, y):
    return x + y

apply_async(add, (2, 3), callback=print_result)
# Got: 5
apply_async(add, ('hello', 'world'), callback=print_result)
# Got: helloworld


現(xiàn)在我們希望回調(diào)函數(shù)print_reuslt()能夠接受更多的參數(shù),比如其他變量或者環(huán)境狀態(tài)信息。比如我們想讓print_result()函數(shù)每次的打印信息都包括一個(gè)序列號(hào),以表示這是第幾次被調(diào)用,如[1] ...、[2] ...這樣。首先我們想到,可以用額外的參數(shù)在回調(diào)函數(shù)中攜帶狀態(tài),然后用partial()來處理參數(shù)個(gè)數(shù)問題:

class SequenceNo:
    def __init__(self) -> None:
        self.sequence = 0

def handler(result, seq):
    seq.sequence += 1
    print("[{}] Got: {}".format(seq.sequence, result))

seq = SequenceNo()
from functools import partial
apply_async(add, (2, 3), callback=partial(handler, seq=seq)) 
# [1] Got: 5
apply_async(add, ('hello', 'world'), callback=partial(handler, seq=seq))
# [2] Got: helloworld

看起來整個(gè)代碼有點(diǎn)松散繁瑣,我們有沒有什么更簡(jiǎn)潔緊湊的方法能夠處理這個(gè)問題呢?答案是直接使用和其他類綁定的方法(bound-method)。比如面這段代碼就將print_result做為一個(gè)類的方法,這個(gè)類保存了計(jì)數(shù)用的ack序列號(hào),每當(dāng)調(diào)用print_reuslt()打印一個(gè)結(jié)果時(shí)就遞增1:

class ResultHandler:
    def __init__(self) -> None:
        self.sequence = 0
    def handler(self, result):
        self.sequence += 1
        print("[{}] Got: {}".format(self.sequence, result))

apply_async(add, (2, 3), callback=r.handler) 
# [1] Got: 5
apply_async(add, ('hello', 'world'), callback=r.handler) 
# [2] Got: helloworld

還有一種實(shí)現(xiàn)方法是使用閉包,這種方法和使用類綁定方法相似。但閉包更簡(jiǎn)潔優(yōu)雅,運(yùn)行速度也更快:

def make_handler():
    sequence = 0
    def handler(result):
        nonlocal sequence # 在閉包中編寫函數(shù)來修改內(nèi)層變量,需要用nonlocal聲明
        sequence += 1
        print("[{}] Got: {}".format(sequence, result))
    return handler

handler = make_handler()
apply_async(add, (2, 3), callback=handler) 
# [1] Got: 5
apply_async(add, ('hello', 'world'), callback=handler) 
# [2] Got: helloworld

最后一種方法,則是利用協(xié)程(coroutine)來完成同樣的任務(wù):

def make_handler_cor():
    sequence = 0
    while True:
        result = yield
        sequence += 1
        print("[{}] Got: {}".format(sequence, result))

handler = make_handler_cor()
next(handler) # 切記在yield之前一定要加這一句
apply_async(add, (2, 3), callback=handler.send) #對(duì)于協(xié)程來說,可以使用它的send()方法來做為回調(diào)函數(shù)
# [1] Got: 5
apply_async(add, ('hello', 'world'), callback=handler.send)
# [2] Got: helloworld

到此這篇關(guān)于Python技巧匿名函數(shù)、回調(diào)函數(shù)和高階函數(shù) 的文章就介紹到這了,更多相關(guān)Python匿名函數(shù)、回調(diào)函數(shù)和高階函數(shù) 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • TensorFlow:將ckpt文件固化成pb文件教程

    TensorFlow:將ckpt文件固化成pb文件教程

    今天小編就為大家分享一篇TensorFlow:將ckpt文件固化成pb文件教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-02-02
  • python對(duì)矩陣進(jìn)行轉(zhuǎn)置的2種處理方法

    python對(duì)矩陣進(jìn)行轉(zhuǎn)置的2種處理方法

    這篇文章主要介紹了python對(duì)矩陣進(jìn)行轉(zhuǎn)置的2種處理方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • Pandas條件篩選與組合篩選的使用

    Pandas條件篩選與組合篩選的使用

    本文主要介紹了Pandas條件篩選與組合篩選的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • python+mysql實(shí)現(xiàn)個(gè)人論文管理系統(tǒng)

    python+mysql實(shí)現(xiàn)個(gè)人論文管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了python+mysql實(shí)現(xiàn)個(gè)人論文管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • 獨(dú)特的python循環(huán)語(yǔ)句

    獨(dú)特的python循環(huán)語(yǔ)句

    本文主要給大家介紹的是Python循環(huán)語(yǔ)句與其他編程語(yǔ)言中的循環(huán)語(yǔ)句不同的地方,非常的獨(dú)特,有需要的小伙伴可以參考下
    2016-11-11
  • python連接oracle數(shù)據(jù)庫(kù)實(shí)例

    python連接oracle數(shù)據(jù)庫(kù)實(shí)例

    這篇文章主要介紹了python連接oracle數(shù)據(jù)庫(kù)的方法,實(shí)例講述了連接Oracle數(shù)據(jù)庫(kù)的具體步驟及常見的問題,需要的朋友可以參考下
    2014-10-10
  • python xlwt模塊的使用解析

    python xlwt模塊的使用解析

    這篇文章主要介紹了python xlwt模塊的使用解析,幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下
    2021-04-04
  • Python爬蟲運(yùn)用正則表達(dá)式的方法和優(yōu)缺點(diǎn)

    Python爬蟲運(yùn)用正則表達(dá)式的方法和優(yōu)缺點(diǎn)

    這篇文章主要給大家介紹了關(guān)于Python爬蟲運(yùn)用正則表達(dá)式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Python具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • python 循環(huán)讀取txt文檔 并轉(zhuǎn)換成csv的方法

    python 循環(huán)讀取txt文檔 并轉(zhuǎn)換成csv的方法

    今天小編就為大家分享一篇python 循環(huán)讀取txt文檔 并轉(zhuǎn)換成csv的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-10-10
  • Pycharm-community-2021版安裝和配置

    Pycharm-community-2021版安裝和配置

    pycharm-community-PyCharm是一種Python IDE,帶有一整套可以幫助用戶在使用Python語(yǔ)言開發(fā)時(shí)提高其效率的工具,本文就來介紹一下Pycharm-community-2021版安裝和配置,感興趣的可以了解一下
    2023-11-11

最新評(píng)論