高階函數&&裝飾器

高階函數&&裝飾器

高階函數
  • first class object
    • 函數在python中是一等公民
    • 函數也是對象,可調用的對象
    • 函數可以作為普通比變量、參數、返回值
  • 高階函數
    • 數學概念:y = g(f(x))
    • 在數學和計算機科學中,高階函數應當是至少滿足下面一個條件的函數
    • 接受一個或多個函數作為參數
    • 輸出一個函數,return 函數
  • 舉例:
def counter(base):
def inc(step=1):
#nonlocal base#不加此行會有什么影響?調用時會提示base無定義
base += step
return base
return inc
foo = counter(10)
foo()#將變量聲明成nonlocal即可調用
UnboundLocalError Traceback (most recent call last)
<ipython-input-336-624891b0d01a> in <module>()
—-> 1 foo()
<ipython-input-334-2a9fcd38455a> in inc(step)
2 def inc(step=1):
3 #nonlocal base
—-> 4 base += step
5 return base
6 return inc
UnboundLocalError: local variable ‘base’ referenced before assignment
注意:大的對象都是在堆里面創建
f1 = counter(5)和f2 = counter(5),相等嗎?
單純counter是一樣的,但是由于返回值時一個新的對象inc,而inc相當于常量在堆中創建f1 、 f2在堆中為不同的空間
自定義sort函數
#代碼一:
def sort(iterable):#定義函數
ret = []#定義排序的空列表
for x in iterable:#循環列表
for i,y in enumerate(ret):
if x > y:#找到大的就地插入。如果x<y則升序
ret.insert(i,x)#降序
break
else:#不大于,說明值小則尾部追加
ret.append(x)
return ret
print(sort([4,3,4,5,2,1,9,8]))
#代碼二:用一個參數控制順序
def sort(iterable,reverse=False):#定義函數
ret = []#定義排序的空列表
for x in iterable:#循環列表
for i,y in enumerate(ret):
flag = x > y if not reverse else x < y#默認是降序,去掉not則為升序,此方法是通過傳參實現控制
if flag:#找到大的就地插入。如果x<y則升序
ret.insert(i,x)#降序
break
else:#不大于,說明值小則尾部追加
ret.append(x)
return ret
print(sort([4,3,4,5,2,1,9,8]))
#代碼三:函數參數實現:
def sort(iterable,fn=lambda a,b:a<b):#使用匿名函數傳參
ret = []
for x in iterable:
for i,y in enumerate(ret):
#fn = lambda x,y:x>y#此方法也闊以
if fn(x,y):#函數調用獲取返回值,控制參數即可
ret.insert(i,x)
break
else:
ret.append(x)
return ret
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)#就地修改
lst = [4,3,4,5,2,1,9,8]
sorted(lst,key=lambda x:6-x)
print(lst)
lst.sort(key=lambda x:6-x)
print(lst)
[4, 3, 4, 5, 2, 1, 9, 8]
[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)))
[1, 3, 5, 7, 9]
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) 形式雖不同但是結果是一樣的
#舉例:將加法函數柯里化
def add(x,y):
return x+y
add(4,5)#原始加法函數
def add(x):
def add2(y):
return x+y
return add2#重點:讓add的返回值去調用add2這樣就可以關聯xy
add(4)(5)#柯里化加法函數
  • 通過嵌套函數就可以把函數轉化成柯里化函數

裝飾器
  • 應用場景:
    • 一個加法函數,想增強它的功能,能夠輸出被調用過以及調用的參數信息
    • def add(x,y):
      • retrun x + y
    • 現增加輸出功能
    • def add(x,y):
      • print(“call add,x + y”)
      • return x+y
    • 上面的函數是完成了需求,但是有以下缺點:
      • 打印語句的耦合太高
      • 加法函數屬于業務功能,而輸出信息的功能屬于非業務代碼,不該放在業務函數加法中,這屬于代碼侵入
  • 下列代碼做到了業務功能分離,但是fn函數調用參數是個問題
def add(x,y):
return x+y
def logger(fn):
print(“begin”)
x = fn(4,5)##參數寫到代碼里面不靈活
print(“end”)
return x
print(logger(add))
  • 通過柯里化可以使用傳參靈活實現代碼控制
def add(x,y):
return x+y
def logger(fn):#此處也可寫成(fn,*args,**kwargs)來完成傳參需求
def biu(x,y):
print(“begin”)
x = fn(x,y)
print(“end”)
return x
return biu
print(logger(add)(4,5))
  • 裝飾器是python提供的語法糖
def logger(fn):
def biu(x,y):
print(“begin”)
x = fn(x,y)
print(“end”)
return x
return biu
@logger#裝飾器將下方緊鄰的函數作為參數傳遞給裝飾器函數,add為wrapped,fn為wrapper。等價于 add = logger(add),所以add是biu的對象
def add(x,y):
return x+y
print(add(34,32))
  • 裝飾器(無參)
    • 他是一個函數
    • 函數作為他的形參
    • 返回值也是一個函數
    • 可以使用@functionname方式,簡化調用
    • 裝飾器是高階函數,但裝飾器是對傳入函數的功能的裝飾(功能增強)
  • 裝飾器造成的問題:
    • 原函數對象的屬性都被替換了,而使用裝飾器,我們需要查看被裝飾函數的屬性如何解決?
#解決方法一:定義一個新函數,作用:將被修飾函數的屬性重新賦予修飾函數
def copy_properties(src,dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
def logger(fn):
def wrapper(*args,**kwargs):
‘I am wrapper’
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
return x
copy_properties(fn,wrapper)
return wrapper
@logger#add = logger(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
return x+y
print(‘name={},doc={}’.format(add.__name__,add.__doc__))
#通過copy_properties函數將被包裝函數的屬性覆蓋到包裝函數
#凡是被裝飾器的函數都需要復制這些屬性,這個函數很通用
#可以將復制屬性的函數構建成裝飾器函數,帶參裝飾器如下方法:
#解決方法二:帶參裝飾器:
def copy_properties(src):
def copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return copy
def logger(fn):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
‘I am wrapper’
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
return x
return wrapper
@logger#add = logger(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
return x+y
print(‘name={},doc={}’.format(add.__name__,add.__doc__))
  • 需求:獲取函數的執行時長,對時長超過閾值的函數記錄一下
import datetime
import time
def copy_properties(src):
def copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return copy
def logger(duration):
def _logger(fn):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
‘I am wrapper’
start = datetime.datetime.now()
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
delta = (datetime.datetime.now() – start).total_seconds()
print(‘so slow’) if delta > duration else print(‘so fast’)
return x
return wrapper
return _logger
@logger(5) #add = logger(5)(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
time.sleep(2)
return x+y
print(add(5,6))
  • 帶參裝飾器:
    • 他是一個函數
    • 函數作為他的形參
    • 返回值是一個不帶參的裝飾器函數
    • 返回@functionname(參數列表)方式調用
    • 可以看做在裝飾器外層又加了一層函數
  • 將記錄的功能提取出來,這樣就可以通過外部提供的函數來靈活控制
import datetime
import time
def copy_properties(src):
def copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return copy
def logger(duration,func=lambda name,duration: print(‘{} took {}s’.format(name,duration))):
def _logger(fn):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
‘I am wrapper’
start = datetime.datetime.now()
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
delta = (datetime.datetime.now() – start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return x
return wrapper
return _logger
@logger(5) #add = logger(5)(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
time.sleep(6)
return x+y
print(add(5,6))

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
import time
import functools
def logger(duration,func=lambda name,duration: print(‘{} took {}s’.format(name,duration))):
def _logger(fn):
@copy_properties(fn)# wrapper = copy_properties(fn)(wrapper)
def wrapper(*args,**kwargs):
‘I am wrapper’
start = datetime.datetime.now()
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
delta = (datetime.datetime.now() – start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return x
return functools.update_wrapper(wrapper,fn)
return _logger
@logger(5) #add = logger(5)(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
time.sleep(2)
return x+y
print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,add.__doc__,sep=’\n’)
##使用裝飾器調用functools
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)
def wrapper(*args,**kwargs):
‘I am wrapper’
start = datetime.datetime.now()
print(‘begin’)
x = fn(*args,**kwargs)
print(‘end’)
delta = (datetime.datetime.now() – start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return x
return wrapper
return _logger
@logger(5) #add = logger(5)(add)–>return wrapper
def add(x,y):
”’This is a function for add”’
time.sleep(2)
return x+y
print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,add.__doc__,sep=’\n’)

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

(0)
Thunk_LeeThunk_Lee
上一篇 2017-10-23 22:58
下一篇 2017-10-24 21:04

相關推薦

  • OpenSSL 的使用詳解

    OpenSSL 是一個開源項目,其組成主要包括一下三個組件:     openssl:多用途的命令行工具     libcrypto:加密算法庫     libssl:加密模塊應用庫,實現了ssl及tls openssl可以實現:秘鑰證書管…

    Linux干貨 2016-09-23
  • 網絡服務基礎理論

    最近學習了有關網絡管理的有關知識,所以給也在學習這些知識的童鞋分享一下我的一些經驗。 聽了這幾天課,總結了幾個前提。 有關網絡的通信均是基于數據傳輸的,且數據均是二進制格式的流式數據。 在網絡中需要把大包分成小包, 每傳一個包都需要有三個地址,由內向外為端口號、IP地址、MAC地址。端口號確定應用,IP確定網段,MAC確定廣播域中的某個網卡。 OSI模型 &…

    Linux干貨 2017-09-01
  • RPM程序包管理

           RPM的全名“RedHat Package Manager”,當初這個是RedHad公司開發的一款程序包管理套件,隨著RPM的不斷完善,RPM逐漸成為眾多linux發行版程序包管理器的代名詞,那什么是RPM,為什么會用到RPM這個程序包管理工具那,接下來我一一揭曉。  &nb…

    Linux干貨 2015-11-04
  • LVM邏輯卷管理

    本次內容         1、什么是LVM         2、為什么使用LVM         3、LVM的操作命令 …

    Linux干貨 2016-09-02
  • test

    test the URL.

    Linux干貨 2016-12-03
  • lamp簡介

    lamp 構建一個有著動態服務器端技術的網站 LAMP基礎 1)開源領域LAMP組成: L:Linux;A:apache (httpd);M:mysql, mariadb;P:php, perl, python httpd:接收用戶的web請求;靜態資源則直接響應;動態資源為php腳本交由php來運行; php:運行p…

    Linux干貨 2016-11-07
欧美性久久久久