- 高階函數和裝飾器
- 高階函數 :
滿足以下條件之一的稱為高階函數
- 接受一個或多個函數作為參數
- 輸出一個函數
高階函數舉例:
def counter(base):
def inc(step=1):
nonlocal base
base += step
return base
return inc
1)自定義sort函數
def sort(itertable):
ret = []
for i in itertable :
#print(i)
for m,n in enumerate(ret):
if i < n :
ret.insert(m,i)
break
else :
ret.append(i)
return ret
sort([1,9,8,7,5,6,4,2,3])
2)內建函數-高階函數
sorted 排序????????? sorted(iterable[, key][, reverse])
filter?? 過濾數據? filter(function, iterable) –> filter object
map?? 映射???????? map(func, *iterables) –> map object
sorted函數返回一個新的list 而list.sort是就地修改的
filter(function, iterable)
過濾可迭代對象的元素,返回一個迭代器
function一個具有一個參數的函數,返回bool例如,過濾出數列中能被3整除的數字
list(filter(lambda x: x%3==0, [1,9,55,150,-3,78,28,123]))
map(function, *iterables) –> map object
對多個可迭代對象的元素按照指定的函數進行映射,返回一個迭代器
list(map(lambda x:2*x+1, range(5)))
dict(map(lambda x: (x%5,x) , range(500)))
- 柯里化
柯里化指的是將原來接受兩個參數的函數變成新的接受一個參數的函數的過程。新的函數返回一個以原有第二個參數為參數的函數
z = f(x,y)轉換成z = f(x)(y)
舉例
def add(x,y):
return x+y
add(4,6)
轉換
def add(x)
def _add(y)
return x+y
return _add
add(4)(6)
三 裝飾器
裝飾器語法糖 @
1)無參裝飾器 :
a)他是一個函數
b)函數作為形參
c)返回值也是一個函數
d)可以使用@functiname的方式,簡化調用
無參裝飾器例
import time
import datetime
def logger(fn):
def wrapper(*args,**kwargs) :
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() – start).total_seconds()
print(delta)
return ret
return wrapper
@logger
def add(x,y):
”’This is an add
function”’
time.sleep(2)
return x * y
print(add(4,6))
2)裝飾器和高階函數
裝飾器是高階函數,但是裝飾器是對傳入函數的功能的增強。
- 帶參裝飾器
- 是一個函數
- 函數作為他的形參
- 返回值是一個不帶參的裝飾器函數
- 使用@functionname方式調用
- 可以看作是在裝飾器外層又加了一層函數
import time
import datetime
def copy_(str):
def _inc(dest):
dest.__name__ = str.__name__
dest.__doc__ = str.__doc__
return _inc
def logger(duration,func = lambda name,duration:print(‘{} tooks {} s’.format(name,duration))):
def? _logger(fn):
@copy_(fn)
def wrapper(*args,**kwargs) :
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() – start).total_seconds()
print(‘fast’) if delta < duration else print(‘slow’)
#print(delta)
func(fn.__name__,delta)
return ret
return wrapper
return _logger
@logger(3)
def add(x,y):
”’This is an add
function”’
time.sleep(2)
return x * y
print(add(4,6))
print(add.__name__,add.__doc__)
四,functools模塊
functools.update_wrapper(wrapper, wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
@functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
- 類似copy_properties功能
- wrapper 包裝函數、被更新者,wrapped 被包裝函數、數據源
- 元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性
‘__module__’, ‘__name__’, ‘__qualname__’, ‘__doc__’, ‘__annotations__’
模塊名、名稱、限定名、文檔、參數注解 - 元組WRAPPER_UPDATES中是要被更新的屬性,__dict__屬性字典
- 增加一個__wrapped__屬性,保留著wrapped函數
兩個函數的對比如下
函數一
import datetime, time, functools
def logger(duration, func=lambda name, duration: print(‘{} took {}s’.format(name, duration))):
def _logger(fn):
def wrapper(*args,**kwargs):
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 functools.update_wrapper(wrapper, fn)
return _logger
@logger(5) # add = logger(5)(add)
def add(x,y):
time.sleep(1)
return x + y
print(add(5, 6), add.__name__, add.__doc__, sep=’\n’)
函數二
import datetime, time, functools
def logger(duration, func=lambda name, duration: print(‘{} took {}s’.format(name, duration))):
def _logger(fn):
@functools.wraps(fn)
def wrapper(*args,**kwargs):
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(5) # add = logger(5)(add)
def add(x,y):
”’This is a add”’
time.sleep(1)
return x + y
print(add(5, 6), add.__name__, add.__doc__, sep=’\n’)
一般用第二種
五 參數注解,inspe模塊
- 函數注解
- 對函數的參數進行類型注解
- 對函數的返回值進行類型注解
- 只對函數參數做一個輔助的說明,并不對函數參數進行類型檢查
- 提供給第三方工具,做代碼分析,發現隱藏的bug
- 函數注解的信息,保存在__annotations__屬性中
def add(x:int , y:int) -> int :
”’
:param x: int
:param y: int
:return: int
”’
return x + y
print(help(add))
print(add(4, 5))
print(add(‘mag’, ‘edu’))
print(add.__annotations__)
2)inspect模塊
a)signature(callable),獲取簽名(函數簽名包含了一個函數的信息,包括函數名、它的參數類型、它所在的類和名稱空間及其他信息)
import inspect
def add(x:int, y:int, *args,**kwargs) -> int:
return x + y
sig = inspect.signature(add)
print(sig, type(sig)) # 函數簽名
print(‘params : ‘, sig.parameters) # OrderedDict
print(‘return : ‘, sig.return_annotation)
print(sig.parameters[‘y’], type(sig.parameters[‘y’]))
print(sig.parameters[‘x’].annotation)
print(sig.parameters[‘args’])
print(sig.parameters[‘args’].annotation)
print(sig.parameters[‘kwargs’])
print(sig.parameters[‘kwargs’].annotation)
執行結果:
(x:int, y:int, *args, **kwargs) -> int <class ‘inspect.Signature’>
params :? OrderedDict([(‘x’, <Parameter “x:int”>), (‘y’,<Parameter “y:int”>), (‘args’, <Parameter “*args”>), (‘kwargs’, <Parameter “**kwargs”>)])
return :? <class ‘int’>
y:int <class ‘inspect.Parameter’>
<class ‘int’>
*args
<class ‘inspect._empty’>
**kwargs
<class ‘inspect._empty’>
b)parame對象
- 保存在元組中,是只讀的
- name,參數的名字
- annotation,參數的注解,可能沒有定義
- default,參數的缺省值,可能沒有定義
- empty,特殊的類,用來標記default屬性或者注釋annotation屬性的空值
- kind,實參如何綁定到形參,就是形參的類型
- POSITIONAL_ONLY,值必須是位置參數提供(python未提供)
- POSITIONAL_OR_KEYWORD,值可以作為關鍵字或者位置參數提供
- VAR_POSITIONAL,可變位置參數,對應*args
- KEYWORD_ONLY,keyword-only參數,對應*或者*args之后的出現的非可變關鍵字參數
- VAR_KEYWORD,可變關鍵字參數,對應**kwargs
sig.parameters返回一個有序字典OrderedDict
import inspect
def add(x, y:int=7, *args, z, t=10,**kwargs) -> int:
return x + y
sig = inspect.signature(add)
print(sig)
print(‘params : ‘, sig.parameters) # 有序字典
print(‘return : ‘, sig.return_annotation)
print(‘~~~~~~~~~~~~~~~~’)
for i, item in enumerate(sig.parameters.items()):
name, param = item
print(i+1, name, param.annotation, param.kind, param.default)
print(param.default is param.empty, end=’\n\n’)
執行結果:
(x, y:int=7, *args, z, t=10, **kwargs) -> int
params :? OrderedDict([(‘x’, <Parameter “x”>), (‘y’, <Parameter “y:int=7”>), (‘args’, <Parameter “*args”>), (‘z’, <Parameter “z”>), (‘t’, <Parameter “t=10”>), (‘kwargs’, <Parameter “**kwargs”>)])
return :? <class ‘int’>
~~~~~~~~~~~~~~~~
1 x <class ‘inspect._empty’> POSITIONAL_OR_KEYWORD <class ‘inspect._empty’>
True
2 y <class ‘int’> POSITIONAL_OR_KEYWORD 7
False
3 args <class ‘inspect._empty’> VAR_POSITIONAL <class ‘inspect._empty’>
True
4 z <class ‘inspect._empty’> KEYWORD_ONLY <class ‘inspect._empty’>
True
5 t <class ‘inspect._empty’> KEYWORD_ONLY 10
False
6 kwargs <class ‘inspect._empty’> VAR_KEYWORD <class ‘inspect._empty’>
True
業務應用(包裝成裝飾器)
import inspect
def check(fn):
def wrapper(*args, **kwargs):
sig = inspect.signature(fn)
params = sig.parameters
values = list(params.values())
for i,p in enumerate(args):
param = values[i]
if param.annotation is not param.empty and not isinstance(p, param.annotation):
print(p,’!==’,values[i].annotation)
for k,v in kwargs.items():
if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation):
print(k,v,’!===’,params[k].annotation)
return fn(*args, **kwargs)
return wrapper
@check
def add(x, y:int=7) -> int:
return x + y
#調用測試
print(add(20,10))
print(add(‘x‘,’y‘)) #會打印y !== <class ‘int’>
六偏函數 partia,lru_cache裝飾器
- patial方法
- 偏函數,把函數部分的參數固定下來,相當于為部分的參數添加了一個固定的默認值,形成一
個新的函數并返回 - 從partial生成的新函數,是對原函數的封裝
- 偏函數,把函數部分的參數固定下來,相當于為部分的參數添加了一個固定的默認值,形成一
partial 簡易源碼
def partial(func,*args,**keywords):
def newfunc(*fargs,**fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
print((args + fargs),newkeywords)
return func(*(args + fargs),**newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
def add(x, y, *args) -> int:
#print(args)
return x + y
newadd = partial(add,1,3,6,5)#func(1,3,6,5,15)
newadd(15)
如上定義partial的話ruturn的func為func(1,3,6,5,15)所以函數add的return值不變但是如果定義partial為partial(add,x = 4,y = 5)的話,partial的return就變成了一個字典{‘x‘:4,’y’:5}此時如果newadd(1,3)就會報錯x,y重復賦值此時只能使用關鍵字傳參。
2)@functools.lru_cache(maxsize=128, typed=False)
- Least-recently-used裝飾器。lru,最近最少使用。cache緩存
- 如果maxsize設置為None,則禁用LRU功能,并且緩存可以無限制增長。當maxsize是二的冪
時,LRU功能執行得最好 - 如果typed設置為True,則不同類型的函數參數將單獨緩存。例如,f(3)和f(3.0)將被視為具有不
同結果的不同調用
最適合@ functools.lru_cache(maxsize=128, typed=False)的場景,用遞歸函數求斐波那契數列。
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/96909