高階函數&&裝飾器

高階函數&&裝飾器

高階函數
  • 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
下一篇 2017-10-24

相關推薦

  • 第十二周課堂練習下

    6、在LAMP架構中,請分別以php編譯成httpd模塊形式和php以fpm工作為獨立守護進程的方式來支持httpd,列出詳細的過程 1.模塊方式安裝可見第三節 2.fpm獨立守護進程安裝 [root@LAMP setup]# tar -xf  php-5.6.23.tar.bz2  ##編譯&nb…

    Linux干貨 2017-01-03
  • 馬哥教育網絡班N22期+第6周課程練習

    一、總結vim編輯器的使用方法 vim文本編輯器 全屏編輯器,模式化編輯器 vim的教程文件命令:vimtutor vim /path/to/somefile vim模式: 編輯模式(命令模式),默認模式 輸入模式 末行模式 內置的命令行接口; 模式轉換: 編輯模式—>輸入模式: i:insert, 在光標所在處前方輸入,轉為輸入模式 a:append…

    Linux干貨 2016-10-09
  • N25-第五周作業

    第五周作業 1、顯示當前系統上root、fedora或user1用戶的默認shell; grep "^\(root|fedora|user1\)" /etc/passwd | cut -d: -f7 2、找出/etc/rc.d/init.d/functions文件中某單詞后面跟一組小括號的行,形如:hello(); grep "…

    Linux干貨 2017-02-14
  • vim基本總結

    ASCII可以將計算機存儲的0或1轉成我們認識的文字。在Linux中,絕大部分的配置文件都是以ASCII的純文本形態存在。通過文本編輯器,可以實現對這些文本文件的更改。常風的文本編輯器有emacs, pico,nano,joe與vi(vim是vi的升級版)等。那么為會么要學vi呢? l 因為vi是內置編輯器,系統安裝好就有了 l 很多軟件…

    2017-08-05
  • 馬哥教育網絡班22期-第13周博客作業

    第13周博客作業 1、建立samba共享,共享目錄為/data,要求:(描述完整的過程)   1)共享名為shared,工作組為zhucke;   2)添加組develop,添加用戶gentoo,centos和ubuntu,其中gentoo和centos以develop為附加組,ubuntu不屬于develop組;密碼均為用戶名; &nb…

    Linux干貨 2016-12-26
  • centos7配置bindDNS解析服務

    首先來概述一下 通俗一點理解呢,DNS就是把域名轉換成IP的一種服務(反向則是IP轉換成域名).以方便人們的記憶,就好比記住一個代表性的英文要比記住電話號碼快得多,通常我們將這種服務稱之為解析.現在一般公網都有萬網 騰訊云這些機構,DNS這一塊我們不需要負責.但是在現在企業的內部網則就需要我們來搭建DNS服務器了.本次采用的bind工具進行域名的解析。 1.…

    Linux干貨 2018-03-26
欧美性久久久久