高階函數和裝飾器

  • 高階函數和裝飾器
  1. 高階函數 :

滿足以下條件之一的稱為高階函數

  • 接受一個或多個函數作為參數
  • 輸出一個函數

高階函數舉例:

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)裝飾器和高階函數

裝飾器是高階函數,但是裝飾器是對傳入函數的功能的增強。

  • 帶參裝飾器
    1. 是一個函數
    2. 函數作為他的形參
    3. 返回值是一個不帶參的裝飾器函數
    4. 使用@functionname方式調用
    5. 可以看作是在裝飾器外層又加了一層函數

 

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模塊

  • 函數注解
    1. 對函數的參數進行類型注解
    2. 對函數的返回值進行類型注解
    3. 只對函數參數做一個輔助的說明,并不對函數參數進行類型檢查
    4. 提供給第三方工具,做代碼分析,發現隱藏的bug
    5. 函數注解的信息,保存在__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對象

  1. 保存在元組中,是只讀的
  2. name,參數的名字
  • annotation,參數的注解,可能沒有定義
  1. default,參數的缺省值,可能沒有定義
  2. empty,特殊的類,用來標記default屬性或者注釋annotation屬性的空值
  3. kind,實參如何綁定到形參,就是形參的類型
    1. POSITIONAL_ONLY,值必須是位置參數提供(python未提供)
    2. POSITIONAL_OR_KEYWORD,值可以作為關鍵字或者位置參數提供
    3. VAR_POSITIONAL,可變位置參數,對應*args
    4. KEYWORD_ONLY,keyword-only參數,對應*或者*args之后的出現的非可變關鍵字參數
    5. 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方法
    1. 偏函數,把函數部分的參數固定下來,相當于為部分的參數添加了一個固定的默認值,形成一
      個新的函數并返回
    2. 從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)

  1. Least-recently-used裝飾器。lru,最近最少使用。cache緩存
  2. 如果maxsize設置為None,則禁用LRU功能,并且緩存可以無限制增長。當maxsize是二的冪
    時,LRU功能執行得最好
  3. 如果typed設置為True,則不同類型的函數參數將單獨緩存。例如,f(3)和f(3.0)將被視為具有不
    同結果的不同調用

最適合@ functools.lru_cache(maxsize=128, typed=False)的場景,用遞歸函數求斐波那契數列。

本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/96909

(0)
陽光和蘇陽光和蘇
上一篇 2018-04-23
下一篇 2018-04-23

相關推薦

  • functools模塊,偏函數partial、緩存LRU

    functools模塊,偏函數partial、緩存LRU

    2018-04-23
  • Ipython封裝解構

    IPython Shell命令 !command 執行shell命令 !ls -l , !touch a.txt file = !ls -l | grep py 魔術方法 使用%開頭的,IPython內置的特殊方法 %magic 格式 %開頭是line magic %% 開頭是cell magic,notebook的cell %alias 定義一個系統命令的…

    2018-04-09
  • 二叉樹的遍歷和堆排序

    二叉樹的遍歷和堆排序

    Python筆記 2018-05-16
  • Python第二周小結

    不知不覺已經正式學習接觸Python兩周了,第二周主要開始了Python內置數據結構的學習,包括從一開始的列表list,元組tuple,字符串string,再到后來的bytes, bytearray, 以及最后的集合set。這些數據結構可以說Python最為基礎的幾種類型,想要用Python寫出漂亮的代碼離不開對他們的熟練掌握與深刻理解。這幾個結構各有各的特…

    Python筆記 2018-03-31
  • DevOps 風向標!DevOps國際峰會6月29日正式啟航!

    DOIS 大會為您呈現互聯網公司與海外企業的實踐經驗與工具技術,聚焦 DevOps 在金融、電信、零售等行業的系統性實踐。在這里我們不空談、不務虛,實實在在的專注DevOps落地。

    2018-05-16
  • 面向對象,魔術方法

    面向對象 一面向對象 什么是面向對象: 一種認識世界、分析世界的方法論。將萬事萬物抽象為類。 類class: 類是抽象的概念,是萬事萬物的抽象,是一類事物的共同集合的集合。 用計算機語言來描述類,就是屬性和方法的集合。 對象instance,object: 對象是類的具象,是一個實體。 每個個體都是抽象類的不同實體。 哲學 一切皆對象 對象是數據和操作的封裝…

    Python筆記 2018-05-14
欧美性久久久久