Python學習筆記五函數式編程(二)

來源:互聯網
上載者:User

標籤:表示   bug   tps   運行   再計算   pytho   ora   引用   函數的參數   

參考教程:廖雪峰官網https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

一、返回函數

高階函數除了可以接受函數作為參數之外,還可以把函數作為傳回值。

通常我們也可以通過以下方式求和:

def calc_sum(*args):    sum=0    for n in args:        sum=sum+n    return sum

但如果有一種情況 ,不需要立刻得出求和結果,而是在後續的代碼中根據需要再計算,這種情況不返回求和的結果,而是返回求和的函數:

def lazy_sum(*args):    def funcsum():        sum=0        for n in args:            sum=sum+n        return sum    return funcsumx=lazy_sum(1,3,5)#傳回值賦給x,x是一個代入(2,3,5)元組局部變數的函數funcsum()#通過輸出可以看出x是一個函數print(x)#需要調用x()時候才輸出求和結果print(x())

對於上例,調用lazy_sum()時候傳入的參數(1,3,5)成為了新建立的funcsum()函數的內部變數,這樣的程式結構也稱為“閉包(Closure)”。

需要注意,即便傳入同樣的參數,返回的函數也是不同的:

x1=lazy_sum(1,3,5)x2=lazy_sum(1,3,5)print(x1==x2)  #False

需要注意的是,返回函數並不立即執行,直到需要調用它的時候才執行,根據執行的時候各個參數和變數的值輸出結果,看下面的例子:

def count():    fs = []    for i in range(1, 4):        def f():             return i*i        fs.append(f)    return fs   #返回一個函數列表f1, f2, f3 = count()fx=count()print(f1())print(f2())print(f3())print(fx[0]())print(fx[1]())print(fx[2]())

以上輸出全為9,是因為在產生(返回)f1,f2,f3和函數列表fx的時候,它們內部的變數是i,而在執行的時候i已經變成了3。所以在使用閉包時需要牢記:返回函數不要引用任何迴圈變數,或者後續會發生變化的變數。

如果一定要使用迴圈,那隻有再建立一個函數,把迴圈變數綁定到其參數上,這樣的綁定在迴圈過程中就實現了,可以保證迴圈後也不變:

def mycount():    def f(j):        def g():            return j*j        return g    fs=[]    for i in range(1,4):        #此時i在迴圈中已經把值傳遞給g函數的參數j了        fs.append(f(i))    return fsmyf=mycount()print(myf[0]())print(myf[1]())print(myf[2]())

練習:

#利用閉包返回一個計數器函數,每次調用它返回遞增整數def createCounter():    #注意這裡使用的變數是一個列表,這是避免一個報錯bug    mycounter=[0]    def counter():        mycounter[0]=mycounter[0]+1        return mycounter[0]    return counter‘‘‘python3中也可以用nonlocal先聲明mycounter這樣就可以避免報錯def createCounter():    mycounter=0    def counter():        nonlocal mycounter        mycounter=mycounter+1        return mycounter    return counter‘‘‘tester=createCounter()while input()!=‘3‘:    print(tester())    # 測試:counterA = createCounter()print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5counterB = createCounter()if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:    print(‘測試通過!‘)else:    print(‘測試失敗!‘)   

 二、匿名函數

在Python中有時候不需要顯示定義函數,這樣也可以避免給函數起名的煩惱。

#關鍵字lambda就表示匿名函數,冒號前面的x是函數的參數#傳回值是x*xf=lambda x:x*xf(5)  #返回25

上面代碼中的lambda運算式等同於下面代碼:

def f(x):    return x*x

一個lambda運算式在map()中的應用:

la=list(map(lambda x:x**2+1,[1,2,3,4,5]))print(la)#輸出[2,5,10,17,26]

練習:

#請用匿名函數改造下面的代碼:def is_odd(n):    return n % 2 == 1L = list(filter(is_odd, range(1, 20)))L1=list(filter(lambda x:x%2==1,range(1,20)))print(L)print(L1)

三、裝飾器

某些情況下,我們需要增強若干個函數的功能,比如列印日誌,但又不希望改動函數內部的代碼,則有一種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)

例如,定義一個能列印日誌的裝飾器:

def log(func):    def wrapper(*args,**kw):        print(‘call %s‘ % func.__name__)        return func(*args,**kw)    return wrapper@logdef add(a,b):    return a+bprint(add(3,6))

輸出結果如下:

call add9

注意到通過‘@‘符號在函數定義前增加了語句,即給該函數增加了一個裝飾器功能。相當於執行了語句:now=log(add)。log(add)返回的是一個函數,該函數的功能不僅會運行函數本身,還運行了裝飾器的代碼。但這裡now變數指向了新的函數,即wrapper。

如果要在裝飾器中本身需要傳入參數,則需要編寫一個返回decorator的高階函數:

def log(text):    def decorator(func):        def wrapper(*args,**kw):            print(‘%s %s():‘ % (text,func.__name__))            return func(*args,**kw)        return wrapper    return decorator@log(‘haha‘)#注意這裡參數的用法def add(a,b):    return a+bprint(add(3,9))print(add.__name__)

我們可以發現,最後輸出的函數名變成了裝飾器內部返回的函數名了,為避免一些依賴函數名的代碼執行錯誤,可以通過Python內建的functools.wraps:

import functoolsdef log(func):    @functools.wraps(func)    def wrapper(*args,**kw):        print(‘call %s:‘ % func.__name__)        return func(*args,**kw)    return wrapper@logdef add(a,b):    return a+bprint(add(3,9))print(add.__name__)

練習:

#請設計一個decorator,它可作用於任何函數上,並列印該函數的執行時間:#-*- coding: utf-8 -*-import time, functoolsdef metric(fn):    @functools.wraps(fn)  #保持原始函數名    def timedeco(*args,**kws):        start_time=time.time()        print(‘START TIME:‘+time.asctime(time.localtime(start_time)))        x=fn(*args,**kws)        end_time=time.time()        print(‘END TIME:‘+time.asctime(time.localtime(end_time)))        print(‘%s executed in %s ms‘ % (fn.__name__, str(end_time-start_time)))        return x    return timedeco        # 測試@metricdef fast(x, y):    time.sleep(1.0012)    return x + y;@metricdef slow(x, y, z):    time.sleep(1.1234)    return x * y * z;f = fast(11, 22)print(f)print(‘\n\n\n‘)s = slow(11, 22, 33)print(s)if f != 33:    print(‘測試失敗!‘)elif s != 7986:    print(‘測試失敗!‘)           

 

Python學習筆記五函數式編程(二)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.