高階函數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,y,reverse):
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