高階函數&&裝飾器
高階函數
- first class object
- 函數在python中是一等公民
- 函數也是對象,可調用的對象
- 函數可以作為普通比變量、參數、返回值
- 高階函數
- 數學概念:y = g(f(x))
- 在數學和計算機科學中,高階函數應當是至少滿足下面一個條件的函數
- 接受一個或多個函數作為參數
- 輸出一個函數,return 函數
- 舉例:
#nonlocal base#不加此行會有什么影響?調用時會提示base無定義
UnboundLocalError Traceback (most recent call last)
<ipython-input-336-624891b0d01a> in <module>()
<ipython-input-334-2a9fcd38455a> in inc(step)
UnboundLocalError: local variable ‘base’ referenced before assignment
注意:大的對象都是在堆里面創建
f1 = counter(5)和f2 = counter(5),相等嗎?
單純counter是一樣的,但是由于返回值時一個新的對象inc,而inc相當于常量在堆中創建f1 、 f2在堆中為不同的空間
自定義sort函數
#代碼一:
for i,y in enumerate(ret):
if x > y:#找到大的就地插入。如果x<y則升序
print(sort([4,3,4,5,2,1,9,8]))
def sort(iterable,reverse=False):#定義函數
for i,y in enumerate(ret):
flag = x > y if not reverse else x < y#默認是降序,去掉not則為升序,此方法是通過傳參實現控制
if flag:#找到大的就地插入。如果x<y則升序
print(sort([4,3,4,5,2,1,9,8]))
def sort(iterable,fn=lambda a,b:a<b):#使用匿名函數傳參
for i,y in enumerate(ret):
#fn = lambda x,y:x>y#此方法也闊以
if fn(x,y):#函數調用獲取返回值,控制參數即可
print(sort([4,3,4,5,2,1,9,8]))
- sorted(iterable[,key][,reverse])排序
- 返回一個新的列表,對一個可迭代帶向的所有元素排序,排序規則為key定義的函數,reverse表示是否排序翻轉
- sorted(lst,key=lambda x:6-x)#返回新列表,此處并沒有修改列表的值而是通過值來決定是大于還是小于,所以此處是倒序排列,大值變小小值變大。
- lst.sort(key=lambda x:6-x)#就地修改
sorted(lst,key=lambda x:6-x)
lst.sort(key=lambda x:6-x)
[9, 8, 5, 4, 4, 3, 2, 1]#結果可證實上面理論部分
filter(function,iterable)
* 過濾可迭代對象元素,返回一個迭代器。惰性求值
* function一個具有一個參數的函數,返回bool
filter(lambda x:x%3 == 0,[1,9,55,150,-3,78,28,123])
<filter at 0x7fe1840e9a20>
list(filter(lambda x:x%3 == 0,[1,9,55,150,-3,78,28,123]))
[9, 150, -3, 78, 123]#此處用列表轉換可以直接獲取結果
map(function,*iterables)—>map object
- 對多個可迭代對象的元素按照指定的函數進行映射,返回一個迭代器
list(map(lambda x:2*x+1,range(5)))
dict(map(lambda x:(x%5,x),range(500)))
{0: 495, 1: 496, 2: 497, 3: 498, 4: 499}#顯示這個結果的原因是key值不允許重復
柯里化Currying
- 定義:將原來接受兩個參數的函數變成新的接受一個參數的函數過程,新的函數返回一個以原有第二個參數為參數的函數
- z = f(x,y)轉換成z = f(x)(y) 形式雖不同但是結果是一樣的
#舉例:將加法函數柯里化
return add2#重點:讓add的返回值去調用add2這樣就可以關聯xy
裝飾器
- 應用場景:
- 一個加法函數,想增強它的功能,能夠輸出被調用過以及調用的參數信息
- def add(x,y):
- 現增加輸出功能
- def add(x,y):
- print(“call add,x + y”)
- return x+y
- 上面的函數是完成了需求,但是有以下缺點:
- 打印語句的耦合太高
- 加法函數屬于業務功能,而輸出信息的功能屬于非業務代碼,不該放在業務函數加法中,這屬于代碼侵入
- 下列代碼做到了業務功能分離,但是fn函數調用參數是個問題
def logger(fn):#此處也可寫成(fn,*args,**kwargs)來完成傳參需求
@logger#裝飾器將下方緊鄰的函數作為參數傳遞給裝飾器函數,add為wrapped,fn為wrapper。等價于 add = logger(add),所以add是biu的對象
- 裝飾器(無參)
- 他是一個函數
- 函數作為他的形參
- 返回值也是一個函數
- 可以使用@functionname方式,簡化調用
- 裝飾器是高階函數,但裝飾器是對傳入函數的功能的裝飾(功能增強)
- 裝飾器造成的問題:
- 原函數對象的屬性都被替換了,而使用裝飾器,我們需要查看被裝飾函數的屬性如何解決?
#解決方法一:定義一個新函數,作用:將被修飾函數的屬性重新賦予修飾函數
def copy_properties(src,dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def wrapper(*args,**kwargs):
copy_properties(fn,wrapper)
@logger#add = logger(add)–>return wrapper
”’This is a function for add”’
print(‘name={},doc={}’.format(add.__name__,add.__doc__))
#通過copy_properties函數將被包裝函數的屬性覆蓋到包裝函數
#凡是被裝飾器的函數都需要復制這些屬性,這個函數很通用
#可以將復制屬性的函數構建成裝飾器函數,帶參裝飾器如下方法:
def copy_properties(src):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
@logger#add = logger(add)–>return wrapper
”’This is a function for add”’
print(‘name={},doc={}’.format(add.__name__,add.__doc__))
- 需求:獲取函數的執行時長,對時長超過閾值的函數記錄一下
import datetime
def copy_properties(src):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
delta = (datetime.datetime.now() – start).total_seconds()
print(‘so slow’) if delta > duration else print(‘so fast’)
@logger(5) #add = logger(5)(add)–>return wrapper
”’This is a function for add”’
- 帶參裝飾器:
- 他是一個函數
- 函數作為他的形參
- 返回值是一個不帶參的裝飾器函數
- 返回@functionname(參數列表)方式調用
- 可以看做在裝飾器外層又加了一層函數
- 將記錄的功能提取出來,這樣就可以通過外部提供的函數來靈活控制
import datetime
def copy_properties(src):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def logger(duration,func=lambda name,duration: print(‘{} took {}s’.format(name,duration))):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
delta = (datetime.datetime.now() – start).total_seconds()
func(fn.__name__,duration)
@logger(5) #add = logger(5)(add)–>return wrapper
”’This is a function for add”’
funtools模塊
- functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENMENTS,updated=WRAPPER_UPDATES)
- 類似copy_properties功能
- wrapper包裝函數,wrapped被包裝函數
- 元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性__module__,__name__,__qualname__,__doc__,__annotations__模塊名、名稱、限定名、文檔、參數注解
- 元組WRAPPER_UPDATES中是要被更新的屬性,__dict__屬性字典
- 增加一個__wrapped__屬性,保留著wrapped函數
import datetime
def logger(duration,func=lambda name,duration: print(‘{} took {}s’.format(name,duration))):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
delta = (datetime.datetime.now() – start).total_seconds()
func(fn.__name__,duration)
return functools.update_wrapper(wrapper,fn)
@logger(5) #add = logger(5)(add)–>return wrapper
”’This is a function for add”’
print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,add.__doc__,sep=’\n’)
##使用裝飾器調用functools
def logger(duration,func=lambda name,duration: print(‘{} took {}s’.format(name,duration))):
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
delta = (datetime.datetime.now() – start).total_seconds()
func(fn.__name__,duration)
@logger(5) #add = logger(5)(add)–>return wrapper
”’This is a function for add”’
print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,add.__doc__,sep=’\n’)
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88039