高階函數、柯里化及裝飾器的使用


高階函數python

數學概念的引入:程序員

例 y = g(f(x))數據結構

數據來源自另外一個函數,y帶到g函數,從而又賦值給y閉包

 

高階函數的特性app

在數學和計算機科學中,高階函數至少知足如下任意一個條件ide

 

在數學中,高階知足如下兩個條件之一:函數

1.接受一個或多個函數做爲參數,對於g來說的話總體算是一個自變量優化

例:將g作爲一個自變量spa

y =g(f1(x),f2(x))操作系統

 

2.輸出一個函數

輸出一個值,這裏的值是函數對象的幀,並不是是函數返回值

 

參數中使用了函數對象,若是輸出的是一個函數,則知足的是一個高階函數

簡單講,參數中出現函數或者return中出現函數則都被稱爲高階函數

 

例:

defcounter(base):

    definc(step=1):

        nonlocalbase

        base+= 1

        returnbase

    returninc()

 

foo =counter(10)

print(foo)

 

foo1 =counter(11)

print(foo1)

 

11

12

 

查看foo和foo1的id地址進行比較

 

foo =counter(10)

print(id(foo))

foo1 =counter(11)

print(id(foo1))

 

494806032

494806064

 

由於inc是counter的某一對象,在賦值的過程當中發生了id更變,因此foo 生成的對象自己不同

能夠做爲一個本地的變量去了解

 

在堆中作對象的建立 ,堆中是亂序的,後期由虛擬機進行垃圾回收;

棧: 由操做系統自動分配釋放 ,存放函數的參數值,局部變量的值等。其操做方式相似於數據結構中的棧。

堆: 通常由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收,分配方式卻是相似於鏈表。

 

在堆中的對象建立,都是爲inc建立的,inc賦值給foo

因此foo1 和 foo的地址不同,當第一次調用counter;

需counter進行壓棧並將參數標記壓棧,建立棧幀,並建立臨時對象inc

inc進行return 交給外部變量,由於最後要retrun,return之後的代碼要被從棧中清空


 

引用是在堆上,並不是在棧,inc所對應的對象須要等值進行賦予,在堆上直接引用地址,因此堆不會被清理

 

自定義sort方法

首先找到一個大值,遍歷x的時候會取一個值並進行對列表遍歷並匹配

若是列表是空的則不會進入循環,直接append到新的列表中

第一次的時候直接進入到ret中,ret如今是1,那麼再獲取31 再與ret中比較,ret重只有一個1

31比1大,那麼在當前位置中插入元素(這裏是與ret的當前位置進行索引插入)

lst =[1,31,4,7,3,21,8]

defsort(items):

    ret= []

    forx in items:        #遍歷lst

        fori,y in enumerate(ret):        #對下標進行標註

            ifx > y:                    #拿出第一個元素依次對比列表中的元素,若是大於當前值則在最前面追加索引

                ret.insert(i,x)   

                break

        else:

            ret.append(x)

    returnret

print(sort(lst))

 

 

 

改進:對其添加默認參數及判斷優化

lst =[1,31,4,7,3,21,8]

defsort(items,reverse=False):

    ret= []

    forx in items:

        fori,y in enumerate(ret):

            flag= x > y if  not reverse else x < y    #flag爲標記,若是反向說明這個是true

            

            ifflag:        # 判斷其布爾值

               ret.insert(i,x)

                break

        else:

            ret.append(x)

    returnret

print(sort(lst))

使用flag 無非是判斷reverse參數的真假值,用於正反向

print(sort(lst,reverse=False))

print(sort(lst,reverse=True))

 

flag= x > y if  not reverse else x < y

至關於:

            ifreverse:

                flag= x > y

            else:

                flag= x < y  

 

 

 

再次改進:

使用嵌套函數實現判斷reverse

lst= [1,31,4,7,3,21,8]

defsort(items,reverse=False):

    defcomp(a,b):                        # 用ab去接受xy 的值

        flag= a > b if reverse else a < b

        returnflag                       # 返回flag的布爾值

    ret= []

    forx in items:

        fori,y in enumerate(ret):

            ifcomp(x,y):                  # 判斷flag的布爾值是x > y仍是y<x

                ret.insert(i,x)

                break

        else:

            ret.append(x)

    returnret

print(sort(lst,reverse=True))

 

再次改進,將通用函數放置函數外部

lst= [1,31,4,7,3,21,8]

defsort(items,reverse=False):

    defcomp(a,b,reverse):        #用ab去接受xy

         flag= a > b if reverse else a < b

    ret= []

    forx in items:

        fori,y in enumerate(ret):

            ifcomp(x,yreverse):

                ret.insert(i,x,reverse)

                break

        else:

            ret.append(x)

    returnret

print(sort(lst,reverse=True))

 

 

再次改進:

經過參數傳參調用外部函數(外部函數爲一個通用函數)進行

在外部函數控制內部函數的排序邏輯,升序或者降序,這樣使得咱們的函數更加靈活

 

lst =[1,31,4,7,3,21,8]

defcomp(a,b):

    returna < b

 

defsort(items,key=comp,reverse=False):

    ret= [ ]

    forx in items:

        fori ,y in enumerate(ret):

            flag= key(x,y) if reverse else key(y,x)

            #if comp(x,y):

            ifflag:

                ret.insert(i,x)

                break

        else:

            ret.append(x)

    returnret

print(sort(lst,reverse=True))

 

 

使用lambda替換comp函數

 

defsort(lst,key=lambda a,b : a < b,reverse=False):        # 經過key的傳參的布爾值來控制升序或降序

    ret= [ ]

    forx in lst:

        fork,v in enumerate(ret):

            flag= key(x,k) if reverse elsekey(k,v)        # 判斷函數的布爾值來替換排序的位置,真爲x>k 假爲x < k

            ifflag:

                ret.insert(k,x)

                break

        else:

            ret.append(x)

    returnret

print(sort(lst,reverse=True))

print(sort(lst,reverse=False))

 

 

 


 

 

總結:

所謂高階函數的參數由外界的傳入;

首先基本函數成型,將其抽取參數,判斷分支結構進行拆解造成外部函數

 

 

內建函數

sorted       排序

filter       過濾數據,只保留想獲取的部分

map          映射

 

sorted  經過key作排序進行規則定義,只能作同類型的數據處理

In [7]:lst = [1,31,4,7,3,21,8]

 

In [8]:sorted(lst,reverse=True)

Out[8]:[31, 21, 8, 7, 4, 3, 1]

 

In [9]:sorted(lst,reverse=False)

Out[9]:[1, 3, 4, 7, 8, 21, 31]

 

In[25]: a = lambda x: 6 -x

In[26]: a(7)

Out[26]:-1

 

In[32]: sorted(lst,key=lambda x:31-x)

Out[32]:[31, 21, 8, 7, 4, 3, 1]

就地修改

In[37]: lst

Out[37]:[1, 31, 4, 7, 3, 21, 8]

In[38]: lst.sort()

In[39]: lst

Out[39]:[1, 3, 4, 7, 8, 21, 31]

 

In[46]: lst.sort(key=lambda x:1)

In[47]: lst

Out[47]:[31, 21, 8, 7, 4, 3, 1]

比較臨時值,追加元素自己,key並非修改原來元素,之是爲了臨時比較得出臨時值

 

filter

 

只能過濾可迭代的對象元素,並返回一個迭代器

返回迭代器說明是同一份數據,function一個具備一個參數的函數,返回一個布爾值

迭代器說明作一些大量數據作一些處理,因此迭代器比較合適

 

In[60]: list( filter(lambda x:x%3 == 0 ,[_ for _ in range(10)]))

Out[60]:[0, 3, 6, 9]

 

a =filter(lambda x: x%3 == 0 ,(_ for _ in range(10)))

#print(list(a))

[0, 3,6, 9]

 

至關於:

lst =[1,2,3,4,5,6]

deffn(x,ret=[]):

    forx in x:

        ifx % 3 == 0:

            ret.append(x)

    returnret

print(fn(lst))

 

至關於咱們自行編寫的sort函數,一個x參數表示對每一個參數進行判斷並過濾,x拿到元素以後查看是否能夠被3整除,由於生成器是惰性求值,因此並不會當即返回

 

map

map可收集n個可迭代對象,並生成迭代器

將各類類型進行迭代,能夠是元組、字典、list 均可以

In[65]: lambda x:x%3==0,[2,3,4,5,6,7,8,9,11,12,13]

Out[65]:(<function __main__.<lambda>>, [2, 3, 4, 5, 6, 7, 8, 9, 11, 12,13])

 

In[66]: map(lambda x:x%3==0,[2,3,4,5,6,7,8,9,11,12,13])

Out[66]:<map at 0x7f22901cb278>

 

In [68]:dict(map(lambda x:(x,x%3==0),[2,3,4,5,6,7,8,9,11,12,13]))

Out[68]:

{2:False,

3:True,

4:False,

5:False,

6:True,

7:False,

8:False,

9:True,

11:False,

12:True,

13:False}

 

In[71]: dict(map(lambda x:(x%3,x),[2,3,4,5,6,7,8,9,11,12,13]))

Out[71]:{0: 12, 1: 13, 2: 11}

 

In[76]: dict(map(lambda x:(x+1,x),range(10)))

Out[76]:{1: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 7, 9: 8, 10: 9}

 

字典生成過程必須是一個二元組

總結:

map主要是生成一個新的值,從一個元素映射到另外一個元素

map 直接轉換,將原來的某個操做添加封裝以另外一種形式返還

 

filter 只保留當前須要的元素,其餘丟棄(python中不會當即修改,而是惰性求值,,由於返回的都是迭代器)

sorted不會就地修改

 

高階函數簡單來說,在參數中出現了函數,二者知足其一則被稱爲高階函數

全部的函數封裝除了嵌套函數(由於須要用到閉包)都須要聽從這樣的原則

 

#將核心邏輯改成可變的,由於函數是一等公民

 

 

柯里化

數學概念,將原來接受兩個參數的函數整合爲一個新的能夠接受一個參數函數的過程

新的函數返回一個原有第二個參數做爲參數的函數

 

z = f(x,y)轉爲 z = f(x)(y)

 

defadd(x):

    def_add(y):

        returnx + y

    return_add

print(add(4)(6))

 

需求來了:

一個加法函數,想加強它的功能,可以被調用過以及調用的參數信息

defadd(x,y):

    returnx + y

 

#改成:

defadd(x,y):

    print('call,x+y')

    returnx + y

 

使用__name__特殊對象來獲取對象名稱

funcname.__name__

 

defadd(x,y,file):

    print('{},{}+ {}'.format(add.__name__,x,y,file=file))

    #print(add.__name__)

    returnx + y

 

add(4,5,file='file')

add,4 +5

 

file是一個形參,後面file以關鍵字參數傳參

 

首先打印語句耦合性過高,與add函數混在一塊兒,

打印函數是否屬於加法?咱們須要將非業務的需求也加入進來,因此須要加入到業務函數是不合適的,這樣的寫法叫作侵入式

 

可是又想對函數進行功能性加強

 

對業務作出分離

 

defadd(x,y):

    returnx + y

 

deflogger(fn):

    print('before')

    ret= fn(4,5)

    print('after')

    returnret

logger(add)

 

 

但這裏的參數依舊是寫死的,改進:

defadd1(x,y):

    returnx + y

 

defadd2(x,y,z):

    returnx + y + z

 

deflogger(fn,*args,**kwargs):

    ret= fn(*args,**kwargs)

    returnret

 

print(logger(add2,3,4,5))

 

 

再次改進:

定義一個函數,進行運算,經過閉包方式調用add進行傳參

並返回結果

 

defadd(x,y,z):

    returnx + y + z

 

deflogger(fn,*args,**kwargs):

    def_logger(*args,**kwargs):

        ret= fn(*args,*kwargs)

        returnret

    return_logger

 

print(logger(add)(1,2,3))

 


 

 

引用裝飾器

等於add1 = logger(add1)

 

對比:

defadd(x,y,z):

    returnx + y + z

 

deflogger(fn,*args,**kwargs):

    def_logger(*args,**kwargs):

       return fn(*args,*kwargs)

    return_logger

 

print(logger(add)(1,2,3))

 

修改成:

deflogger(fn):

    def_logger(*args):            #造成一個閉包

        print('123 43 4')         #爲了看到效果並非直接調用add函數,在這裏直接打印一些內容做爲標記

        returnfn(*args)

    return_logger

@logger

defadd1(*args):

    i= 0

    forx in args:

        i+= x

    returni

 

print(add1(1,2,3,4))

 

12 3 434

10

 

裝飾器 等於 add = logger(add)

裝飾器只能接一個參數,參數就是add標識符

 

裝飾器語法

無參裝飾器

·它是一個函數

·函數做爲它的參數

·返回值也必須是一個函數

 

deflogger(fn):

    defwapper(*args):    

        print('begin')

        x= fn(*args)

        print('end')

        returnx

    returnwapper        

 

@logger

defadd(x,y):

    returnx + y

print(add(1,2))

 

 

裝飾器確定是一個高階函數,不斷傳入函數,並返回一個內層函數

 

 

總結對比

 

defadd1(*args):

    i= 0

    forx in args:

        i+= x

    returni

#########################################

deftest(fn):

    def_test(*args):

        print(111111)

        returnfn(*args)

    return_test

@test

defabc(*args):

    i= 0

    forx in args:

        i+= x

    returni

print(abc(1,2,3))

 

 

 

 

文檔字符串

__doc__string

使用要求:在函數第一個表達式,必須是個字符串

defadd1(x,y):

    ret= x,y

    returnret

add1(1,100)

print(add1.__name__)

add1

 

 

 

defadd1(x,y):

    '''hellopython

enenenen'''

    ret= x,y

    returnret

add1(1,100)

print(add1.__doc__)

 

hellopython

enenenen

 

 

 

函數的幫助自己就是調用__doc__()

 

使用裝飾器改logger,添加說明

 

deflogger(fn):

    def_logger(*args):

        'thiis logger'

        print('logger')

        returnfn(*args)

    return_logger

 

@logger

defadd(x,y):

    returnx + y

print(add.__name__,add.__doc__,sep='\n')

 

_logger

thi islogger

看到這裏打印的是logger函數內的信息

name和doc是對象的屬性,因此,咱們能夠經過一個新的函數對其進行控制

 

 

 

定義一個新的函數用於標記被調函數的屬性

defcopy_pro(src,dst):

    dst.__name__= src.__name__

    src.__doc__= dst.__doc__

 

defcopy_pro(src,dst):

    dst.__name__= src.__name__

    dst.__doc__= src.__doc__

 

deflogger(fn):

    def_logger(*args):

        'thiis logger'

        print('logger')

        returnfn(*args)

    copy_pro(fn,_logger)

    return_logger

 

@logger

defadd(x,y):

    'hahaha'

    returnx + y

 

print(add.__name__,add.__doc__,sep='\n')

add

hahaha

 

 


將 _logger 的__doc__ 和 __name__ 指向了 add.__name__ , add__doc__

 

logger定義過了,源已經賦值給了目的,src被帶入了fn也就是add,doc是add 字符串

這樣就把屬性改成同樣的

 

__qualname__  是函數的限定名稱

print(add.__qualname__,sep='\n')

logger.<locals>._logger

 

 

簡化:將拷貝函數作爲裝飾器進行封裝

被包裝函數的文檔等一切屬性則會消失

 

改造:帶參裝飾器

提供一個函數,被封裝函數屬性copy() 包裝函數屬性

defcopy_pro(src):

    def_copy(dst):

        dst.__name__= src.__name__

        dst.__doc__= src.__doc__

        dst.__qualname__= src.__qualname__

        returndst

    return_copy

 

deflogger(fn):

    @copy_pro(fn)

    defwrapper(*args,**kwargs):

        x= fn(*args,**kwargs)

        returnx

    returnwrapper

 

@logger

defadd(x,y):

    returnx + y

print(add(1,2))

print(add.__qualname__)

 

 

 

python自己提供了拷貝函數

經過自定義函數將被包裝函數屬性覆蓋掉包裝函數

凡被裝飾裝飾的函數都須要複製這些屬性,比較通用

能夠將複製屬性的函數構建成裝飾器函數 -- 帶參裝飾器

 

帶參裝飾器,帶參裝飾器無非加了一層函數

例:將大於50秒的語句篩出

參數值只能傳一個,因此須要與其替換次序

importdatetime

importtime

 

deflogger(t):

    def_logger(fn):

 

        defwrap(*args,**kwargs):

            start= datetime.datetime.now()

            ret= fn(*args,**kwargs)

            duration= int((datetime.datetime.now() - start).total_seconds())

            ifduration > t:

                print("{}took {}".format(fn.__name__,duration.total_seconds()))

 

            returnret

        returnwrap

    return_logger

 

@logger(30)

defadd(x,y):

    time.sleep(5)

    returnx + y

 

print(add(33,y=7))

 

以上就能夠將經過外部提供的函數來靈活的控制輸出

 

 

functools模塊

 

import functools

 

deflogger(fn):

    @functools.wraps(fn)

    def_logger(*args):

        'thiis logger'

        print('logge!!!!!!!!!!!r')

        returnfn(*args)

    #copy_pro(fn,_logger)

    return_logger

 

@logger

defadd(x,y):

    'hahaha'

    returnx + y

 

print(add(2,3))

print(add.__qualname__,add.__doc__,sep='\n')

 

logge!!!!!!!!!!!r

5

add

hahaha

相關文章
相關標籤/搜索