您好,登錄后才能下訂單哦!
?
高階函數(shù)、柯里化、裝飾器、functools、文檔字符串
?
?
目錄
高階函數(shù)... 1
內(nèi)建函數(shù)-高階函數(shù)... 3
currying柯里化:... 4
decorator裝飾器:... 4
裝飾器(無(wú)參):... 7
帶參裝飾器:... 9
functools:... 12
文檔字符串:... 14
?
?
?
函數(shù):
first class object,函數(shù)在python中是一等公民;
函數(shù)也是對(duì)象,可調(diào)用的對(duì)象;
函數(shù)可以作為普通變量、參數(shù)、返回值等;
?
高階函數(shù)
數(shù)學(xué)概念,y=g(f(x));
在數(shù)學(xué)和計(jì)算機(jī)科學(xué)中,高階函數(shù)應(yīng)當(dāng)是至少滿足下面一個(gè)條件的函數(shù):
接受一個(gè)或多個(gè)函數(shù)作為參數(shù);
輸出一個(gè)函數(shù),如return inc;
?
計(jì)數(shù)器:
例:
In [1]: def counter(base):?? #函數(shù)counter()是一個(gè)高階函數(shù)
?? ...:???? def inc(step=1):
?? ...:???????? nonlocal base
?? ...:???????? base += step?? #賦值前引用,用nonlocal解決
?? ...:???????? return base
?? ...:???? return inc
?? ...:
In [2]: foo=counter(10)
In [3]: foo1=counter(10)
In [4]: foo==foo1 ??#foo和foo1不一樣,可理解為inc為counter內(nèi)的標(biāo)識(shí)符,每次counter()運(yùn)行是不同的對(duì)象
Out[4]: False
In [5]: id(foo)
Out[5]: 139964301851096
In [6]: id(foo1)
Out[6]: 139964302182600
In [7]: foo()
Out[7]: 11
In [8]: foo()
Out[8]: 12
In [9]: foo1()
Out[9]: 11
In [10]: foo1()
Out[10]: 12
?
自定義一個(gè)sort函數(shù):
排序問(wèn)題,參照內(nèi)建函數(shù)sorted,自行實(shí)現(xiàn)一個(gè)sort函數(shù)(不使用內(nèi)建函數(shù)),能夠?yàn)榱斜碓嘏判颍?/span>
需求:原列表不變(sorted(lst));原列表改變(lst.sort());
思路:
內(nèi)建函數(shù)sorted返回一個(gè)新列表,可設(shè)置升序降序,可設(shè)置一個(gè)排序的函數(shù),自定義sort函數(shù)也要具備這些功能;
新建一個(gè)列表,遍歷原列表,和新列表的值依次比較決定如何插入到新列表中;
sorted函數(shù)的實(shí)現(xiàn)原理,擴(kuò)展到map、filter函數(shù);
?
例:
def sort(iterable):
??? ret = []
??? for x in iterable:
??????? for i,y in enumerate(ret):
??????????? if x > y:
??????????????? ret.insert(i,x)
??????????????? break
??????? else:
??? ????????ret.append(x)
??? return ret
?
sort([5,3,2,1,4])
#######################
def sort(iterable,reverse=False):
??? ret = []
??? for x in iterable:
??????? for i,y in enumerate(ret):
??????????? flag = x < y if reverse else flag = x > y
??????????? if flag:
??????????????? ret.insert(i,x)?? #找到大的就插入,降序
??????????????? break
??????? else:
??????????? ret.append(x)?? #小的尾部追加
??? return ret
?
sort([5,3,2,1,4],reverse=True)
#######################
def sort(iterable,key=lambda a,b:a<b):?? #默認(rèn)值保存在__defaults__中,只是對(duì)該匿名函數(shù)對(duì)象的引用
??? ret = []
??? for x in iterable:
??????? for i,y in enumerate(ret):
??????????? if key(x,y):
??????????????? ret.insert(i,x)
??????????????? break
??????? else:
??????????? ret.append(x)
??? return ret
?
sort([5,3,2,1,4],lambda a,b:a>b)
#########################
?
?
?
sorted(iterable[,key][,reverse])-->new list,排序,返回一個(gè)新列表,對(duì)一個(gè)可迭代對(duì)象的所有元素排序,排序規(guī)則為key定義的函數(shù),可用reverse指定排序翻轉(zhuǎn);
例:
In [11]: lst = [3,2,4,5,1]
In [12]: sorted(lst,key=lambda x:6-x)?? #內(nèi)置sorted的key可對(duì)每個(gè)元素進(jìn)行變化,該變化只在比較時(shí)用于臨時(shí)值,比較后的新列表中的元素仍然是原列表中的元素
Out[12]: [5, 4, 3, 2, 1]
In [13]: lst = ['a',1,2,3,'b']
In [15]: sorted(lst,key=str)?? #key=str為比較邏輯
Out[15]: [1, 2, 3, 'a', 'b']
?
filter(function,iterable)-->filter object,generator,過(guò)濾數(shù)據(jù),過(guò)濾可迭代的元素,返回一個(gè)迭代器;function是一個(gè)具有一個(gè)參數(shù)的函數(shù),返回bool;
例:
In [16]: filter(lambda x:x%3==0,[1,9,55,150,-3.78,28,123])
Out[16]: <filter at 0x7f4bf9d6f320>
In [17]: list(filter(lambda x:x%3==0,[1,9,55,150,-3.78,28,123]))?? #過(guò)濾出列表中能被3整除的數(shù)字;注意function中要有參數(shù)x,否則報(bào)錯(cuò)
Out[17]: [9, 150, 123]
?
map(function,*iterables)-->map object,generator,映射,對(duì)多個(gè)可迭代對(duì)象的元素按指定的函數(shù)進(jìn)行映射,返回一個(gè)迭代器;
例:
In [18]: map(lambda x:2*x+1,range(5))
Out[18]: <map at 0x7f4bf9d76080>
In [20]: list(map(lambda x:2*x+1,range(5)))?? #可用此方式獲取奇數(shù)、偶數(shù)
Out[20]: [1, 3, 5, 7, 9]
In [21]: dict(map(lambda x:(x%5,x),range(500)))?? #構(gòu)造字典,通過(guò)key已去重(后面覆蓋前面的了)
Out[21]: {0: 495, 1: 496, 2: 497, 3: 498, 4: 499}
?
?
?
指將原來(lái)接受2個(gè)參數(shù)的函數(shù)變成新的接受一個(gè)參數(shù)的過(guò)程,新的函數(shù)返回一個(gè)以原有第二個(gè)參數(shù)為參數(shù)的函數(shù);
z=f(x,y)-->z=f(x)(y),這種形式;
例:
將加法函數(shù)柯里化:
In [22]: def add(x,y):
??? ...:???? return x+y
??? ...:
In [23]: def add(x):
??? ...:???? def _add(y):
??? ...:???????? return x+y
??? ...:???? return _add
??? ...:
?
?
?
裝飾器的用途:
裝飾器是AOP思想的體現(xiàn),aspect oriented programming面向切面編程;
面向?qū)ο笸枰ㄟ^(guò)繼承或組合依賴等方式調(diào)用一些功能,這些功能的代碼往往可能在多個(gè)類中出現(xiàn),如logger,這樣造成代碼的重復(fù),增加了耦合,logger的改變影響所有使用它的類或方法;
而AOP在需要的類或方法上切下,前后的切入點(diǎn)可加入增強(qiáng)的功能,讓調(diào)用者和被調(diào)用者解耦;
這是一種不修改原來(lái)的業(yè)務(wù)代碼,給程序動(dòng)態(tài)添加功能的技術(shù),如logger函數(shù)功能就是對(duì)業(yè)務(wù)函數(shù)增加日志的,而業(yè)務(wù)函數(shù)中應(yīng)把與業(yè)務(wù)無(wú)關(guān)的日志功能剝離干凈;
?
裝飾器應(yīng)用場(chǎng)景:
日志、監(jiān)控、權(quán)限、設(shè)計(jì)、參數(shù)檢查、路由等;
這些功能與業(yè)務(wù)功能無(wú)關(guān),很多業(yè)務(wù)都需要公共功能,所以適合獨(dú)立出來(lái),需要的時(shí)候?qū)δ繕?biāo)對(duì)象增強(qiáng);
?
需求:
加法函數(shù),增強(qiáng)其功能,輸出被調(diào)用過(guò)及調(diào)用的參數(shù)信息;
例:
def add(x,y):
??? print('call: {},{}+{}'.format(add.__name__,x,y))
??? return x+y
?
add(1,2)
注:
此方式完成了需求,但有缺點(diǎn);
打印語(yǔ)句的耦合太高,與定義的函數(shù)緊緊關(guān)聯(lián);
加法函數(shù)屬于業(yè)務(wù)功能,而輸出信息是非業(yè)務(wù)功能代碼,不該放在業(yè)務(wù)函數(shù)中,這稱為侵入式代碼;
##################
def add(x,y):
??? return x+y
?
def logger(fn):
??? print('begin')
??? x=fn(4,5)
??? print('end')
??? return x
?
print(logger(add))
注:
雖做到了業(yè)務(wù)功能分離,但fn函數(shù)調(diào)用傳參是個(gè)問(wèn)題;
##################
del add
?
def add(x,y,*args):
??? return x+y
?
def logger(fn,x,y):
??? print('begin')
??? ret=fn(x,y)
??? print('end')
??? return ret
?
print(logger(add,4,5))
###################
def add(x,y,z):
??? return x+y+z
?
def logger(fn,*args,**kwargs):?? #可變參數(shù),解決了傳參問(wèn)題
??? print('begin')
??? ret=fn(*args,**kwargs)?? #參數(shù)解構(gòu)
??? print('end')
??? return ret
?
print(logger(add,4,z=5,y=6))
###################
def add(x,y,z):
??? return x+y+z
?
def logger(fn):?? #在上例基礎(chǔ)上將logger柯里化
??? def _logger(*args,**kwargs):?? #可變參數(shù)
??????? print('begin')
??????? ret=fn(*args,**kwargs)?? #參數(shù)解構(gòu);閉包(fn為自由變量)
??????? print('end')
??????? return ret
??? return _logger?? #返回內(nèi)部函數(shù)的引用
?
print(logger(add)(4,5,6))
?
foo=logger(add)
print(foo(4,5,6))?? #等價(jià)于print(logger(add)(4,5,6))
?
add=logger(add)?? #等價(jià)于@logger
print(add(4,5,6))
#####################
def logger(fn):?? #如果此處用兩個(gè)參數(shù)def logger(fn,x),后面使用@logger時(shí)會(huì)語(yǔ)法錯(cuò)誤,解決辦法,進(jìn)一步柯里化
??? def _logger(*args,**kwargs):?? #可變參數(shù)
??????? print('begin')
??????? ret=fn(*args,**kwargs)?? #參數(shù)解構(gòu);此處閉包,正因?yàn)殚]包原函數(shù)在@logger(即add=logger(add))被保留下來(lái);此處如果寫為return ret=fn(*args,**kwargs)后面語(yǔ)句將不會(huì)執(zhí)行
??????? print('end')
??????? return ret?? #若無(wú)此行會(huì)破壞原函數(shù)
??? return _logger?? #外層函數(shù)應(yīng)返回內(nèi)層函數(shù)的引用
?
@logger?? #等價(jià)于add=logger(add);第一個(gè)add已指向內(nèi)層函數(shù)_logger一般起名為wrapper包裝函數(shù);第二個(gè)add(即logger(add))為原函數(shù);@logger要放到被包裝函數(shù)的上一行
def add(x,y):
??? return x+y
?
print(add(4,5))?? #此處add不是原函數(shù),而是被包裝后的內(nèi)層函數(shù)(即add=logger(add))
注:
外層函數(shù)應(yīng)返回內(nèi)層函數(shù)的引用;
外層函數(shù)的參數(shù)應(yīng)為要包裝的函數(shù),add=logger(add);
@logger即裝飾器的語(yǔ)法;
?
?
?
它是一個(gè)函數(shù);
函數(shù)作為它的形參;
返回值也是一個(gè)函數(shù);
可以使用@function方式,簡(jiǎn)化調(diào)用;
?
裝飾器和高階函數(shù):
裝飾器是高階函數(shù),但裝飾器是對(duì)傳入函數(shù)的功能的裝飾(功能增強(qiáng));
?
例:
import datetime
import time
?
def logger(fn):
??? def wrapper(*args,**kwargs):
??????? print('args={},kwargs={}'.format(args,kwargs))
??????? start = datetime.datetime.now()
??????? ret = fn(*args,**kwargs)
??????? duration = (datetime.datetime.now()) - start
??????? print('function {} took {}s'.format(fn.__name__,duration.total_seconds()))
??????? return ret
??? return wrapper
?
@logger
def add(x,y):
??? print('=========call add===========')
??? time.sleep(2)
??? return x+y
?
print(add(4,y=5))
?
例:
def copy_properties(src,dst):
??? dst.__name__ = src.__name__
??? dst.__doc__ = src.__doc__
??? dst.qualname__ = src.__qualname__
???
def logger(fn):
??? def wrapper(*args,**kwargs):
??????? '''This is a wrapper'''
??????? print('begin')
??????? ret = fn(*args,**kwargs)
??????? print('end')
??????? return ret
??? copy_properties(fn,wrapper)
??? return wrapper
?
@logger
def add(x,y):
??? '''
??? This is a additional.
???
??? return int
??? x int
??? y int
??? '''
??? ret = x + y
??? return ret
?
print(add.__name__,add.__doc__,add.__qualname__,sep='\n')
#######################
def copy_properties(src):
??? def wrapper(dst):
??????? dst.__name__ = src.__name__
??????? dst.__doc__ = src.__doc__
??????? dst.qualname__ = src.__qualname__
??????? return dst
??? return wrapper
???
def logger(fn):
??? @copy_properties(fn)?? #等價(jià)于wrapper=copy_properties(fn)(wrapper),出現(xiàn)的wrapper可認(rèn)為都是logger內(nèi)的wrapper,copy_properties(fn)即copy_properties內(nèi)的wrapper
??? def wrapper(*args,**kwargs):
??????? '''This is a wrapper'''
??????? print('begin')
??????? ret = fn(*args,**kwargs)
??? ????print('end')
??????? return ret
??? #copy_properties(fn,wrapper)
??? return wrapper
?
@logger
def add(x,y):
??? '''
??? This is a additional.
???
??? return int
??? x int
??? y int
??? '''
??? ret = x + y
??? return ret
?
print(add.__name__,add.__doc__,add.__qualname__,sep='\n')
注:
包裝函數(shù)屬性改為被包裝函數(shù)屬性;
調(diào)用時(shí)用到的是嵌套函數(shù)內(nèi)層wrapper的屬性而不是原函數(shù)add的屬性,在使用copy_properties函數(shù)后,可讓使用者認(rèn)為是原函數(shù)的屬性;
通過(guò)copy_properties函數(shù)將被包裝函數(shù)的屬性覆蓋掉包裝函數(shù);
凡是被裝飾的函數(shù)都要復(fù)制這些屬性,上例很通用;
可以將復(fù)制屬性的函數(shù)構(gòu)建成裝飾器函數(shù),帶參裝飾器;
?
?
?
是一個(gè)函數(shù);
函數(shù)作為它的形參;
返回值是一個(gè)不帶參的裝飾器函數(shù)(不一定);
使用@function(參數(shù)列表)方式調(diào)用;
可以看作在裝飾器外層又加了一層函數(shù);
最多也就三層;
需求:
獲取函數(shù)的執(zhí)行時(shí)長(zhǎng),對(duì)長(zhǎng)超過(guò)閾值的函數(shù)記錄一下;
例:
import datetime
import time
?
def logger(t):
??? def _logger(fn):
??????? def wrapper(*args,**kwargs):
??????????? '''This is a wrapper.'''
??????????? print('args={},kwargs={}'.format(args,kwargs))
??????????? start = datetime.datetime.now()
??????????? ret = fn(*args,**kwargs)
??????????? duration = (datetime.datetime.now() - start).total_seconds()
??????????? if duration > t:
????????????? ??print('function {} took {}s'.format(fn.__name__,duration))
??????????? return ret
??????? return wrapper
??? return _logger
?
@logger(3)?? #等價(jià)于add=logger(3)(add)
def add(x,y):
??? print('===============call add================')
??? time.sleep(4)
??? return x+y
?
print(add(4,5))
###########################
import datetime
import time
?
def copy_properties(src):
??? def wrapper(dst):
??????? dst.__name__ = src.__name__
??????? dst.__doc__ = src.__doc__
??????? dst.__qualname__ = src.__qualname__
??????? return dst
??? return wrapper
?
def logger(duration):
??? def _logger(fn):
??????? @copy_properties(fn)?? #等價(jià)于add=copy_properties(add)(dst)
??????? def wrapper(*args,**kwargs):
??????????? '''This is a wrapper.'''
??????????? start = datetime.datetime.now()
??????????? ret = fn(*args,**kwargs)
??????????? delta = (datetime.datetime.now() - start).total_seconds()
??????????? print('so slow') if delta > duration else print('so fast')
??????????? return ret
??????? return wrapper
??? return _logger
?
@logger(3)?? #等價(jià)于add=logger(3)(add)
def add(x,y):
??? print('===============call add================')
??? time.sleep(2)
??? return x+y
?
print(add(4,5))
##########################
def logger(fn):?? #無(wú)論logger(add)是什么都返回10,語(yǔ)法允許,在裝飾器角度并沒有裝飾什么,把原函數(shù)add給廢掉了
??? return 10
?
@logger?? #add=logger(add)
def add(x,y):
??? return x+y
?
print(add)
############################
import datetime
import time
?
def copy_properties(src):
??? def wrapper(dst):
??????? dst.__name__ = src.__name__
??????? dst.__doc__ = src.__doc__
??????? dst.__qualname__ = src.__qualname__
??????? return dst
??? return wrapper
?
def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):
??? def _logger(fn):
??????? @copy_properties(fn)
??????? def wrapper(*args,**kwargs):
??????????? '''This is a wrapper.'''
??????????? start = datetime.datetime.now()
??????????? ret = fn(*args,**kwargs)
??????????? delta = (datetime.datetime.now() - start).total_seconds()
??????????? if delta > duration:
??????????????? func(fn.__name__,duration)
??????????? return ret
??????? return wrapper
??? return _logger
?
@logger(3)
def add(x,y):
??? print('===============call add================')
??? time.sleep(4)
??? return x+y
?
print(add(4,5))
注:
將記錄的功能提取出來(lái),這樣就可通過(guò)外部提供的函數(shù)來(lái)靈活的控制輸出;
?
?
?
functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
類似copy_properties功能;
wrapper包裝函數(shù),wrapped被包裝函數(shù);
元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性,__module__模塊名,__name__名稱,__qualname__限定名,__doc__文檔,__annotations__參數(shù)注解;
元組WRAPEER_UPDATES中是要被更新的屬性,__dict__屬性字典,注意字典要用update方式;
增加一個(gè)__wrapped__屬性,保留著wrapped函數(shù);
?
例:
import datetime,time,functools
?
def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):
??? def _logger(fn):
??????? def wrapper(*args,**kwargs):
??????????? '''This is a wrapper.'''
??????????? start = datetime.datetime.now()
??????????? ret = fn(*args,**kwargs)
??????????? delta = (datetime.datetime.now() - start).total_seconds()
????????? ??if delta > duration:
??????????????? func(fn.__name__,duration)
??????????? return ret
??????? functools.update_wrapper(wrapper,fn)
??? ????return wrapper
??? return _logger
?
@logger(3)
def add(x,y):
??? print('===============call add================')
??? time.sleep(4)
??? return x+y
?
print(add(4,5),add.__name__,add.__wrapped__,add.__dict__,sep='\n')
#######################
import datetime
import time
import functools
?
def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):
??? def _logger(fn):
??????? @functools.wraps(fn)?? #經(jīng)常用
??????? def wrapper(*args,**kwargs):
??????????? '''This is a wrapper.'''
??????????? start = datetime.datetime.now()
??????????? ret = fn(*args,**kwargs)
??????????? delta = (datetime.datetime.now() - start).total_seconds()
??????????? if delta > duration:
??????????????? func(fn.__name__,duration)
??????????? return ret
??????? return wrapper
??? return _logger
?
@logger(3)
def add(x,y):
??? print('===============call add================')
??? time.sleep(4)
??? return x+y
?
print(add(4,5),add.__name__,add.__wrapped__,add.__dict__,sep='\n')
#######################
import functools
???
def logger(fn):
??? @functools.wraps(fn)
??? def wrapper(*args,**kwargs):
??????? '''This is a wrapper'''
??????? print('begin')
??????? ret = fn(*args,**kwargs)
??????? print('end')
??????? return ret
??? print('{} {}'.format(id(wrapper),id(fn)))
??? return wrapper
?
@logger
def add(x,y):
??? '''
??? This is a additional.
???
??? return int
??? x int
??? y int
??? '''
??? ret = x + y
??? return ret
?
print(add.__name__,add.__doc__,add.__qualname__,sep='\n')
print('*'*50)
print(id(add.__wrapped__))
print(add(4,5))
print(add.__wrapped__(4,5))?? #用包裝函數(shù)調(diào)用,如add(4,5);而不要用被包裝函數(shù)調(diào)用,如add.__wrapped__(4,5)
?
?
?
python的文檔:
在函數(shù)語(yǔ)句塊的第一行,且習(xí)慣是多行的文本,所以多使用三引號(hào),約定使用雙引號(hào);
慣例是首字母大寫,第一行寫概述,空一行,第三行寫詳細(xì)描述;
可使用特殊屬性__doc__訪問(wèn)這個(gè)文檔;
?
例:
In [28]: def add(x,y):
??? ...:???? '''
??? ...:???? This is a funtion of addition.
??? ...:????
??? ...:???? return int
??? ...:???? x int
??? ...:???? y int
??? ...:???? '''
??? ...:???? return x+y
??? ...:
In [29]: print('name={}\ndoc={}'.format(add.__name__,add.__doc__))
name=add
doc=
??? This is a funtion of addition.
???
??? return int
??? x int
??? y int
???
?
In [30]: help(add)
……
?
?
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。