python高階函數與裝飾器

##**高階函數**
– 函數是python中的一等公民
– 函數也是對象,可調用對象
– 函數可以作為普通變量、參數、返回值等等
– 數學概念y = g(f(x))
– 高階函數滿足以下至少一個條件:1.接收一個或多個函數作為參數 2.輸出一個函數

##**舉例(計數器)**
def counter(base):
def inc(step=1):
nonlocal base #base在這里自由變量,閉包。
base += step
return base
return inc
>上述代碼 f1 = counter(5) 和 f2 = counter(5),其f1與f2不相等。他們在堆里面不是同一個對象創建。

##**自定義sort函數(版本1)**
def sort(iterable):
ret = []
for x in iterable:
for i, y in enumerate(ret):
if x > y:
ret.insert(i,x)
break
else:
ret.append(x)
return ret
print(sort([1,2,5,4,2,3,5,6]))

##**自定義sort函數(版本2)**
def sort(iterable, reverse=False): #用一個參數控制順序
ret = []
for x in iterable:
for i, y in enumerate(ret):
flag = x>y if reverse else x<y
if flag:
ret.insert(i,x)
break
else:
ret.append(x)
return ret

##**自定義sort函數(版本3)**
def sort(iterable, fn=lambda a,b : a>b): #函數寫進參數里
ret = [ ]
for x in iterable:
for i, y in enumerate(ret):
if fn(x, y): # 返回一個bool值
ret.insert(i,x)
break
else:
ret.append(x)
return ret
print(sort([1,2,5,4,2,3,5,6]))

##**內建高階函數**
1.排序 :sorted(iterable,[,key][,reverse])
> 返回一個新的列表,對一個可迭代對象的所有元素排序,排序規則為key定義的函數,reverse表示是否排序翻轉。

2.過濾 :filter(function, iterable) –> filter object
> (1) 過濾可迭代對象的元素,返回一個迭代器
(2) function一個具有一個參數的函數,返回bool
(3) 過濾能被3整除的數 list(filter(lambda x: x%3==0, [1,9,55,150,-3,78,28,123]))

3.映射 :map(func, *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))) #相同key的值被覆蓋,所以只有五個
{0: 495, 1: 496, 2: 497, 3: 498, 4: 499}

##**柯里化currying**
– 定義:指的是將原來接收兩個參數的函數變成新的接收一個參數的函數的過程。新的函數返回一個以原有第二個參數為參數的函數
##舉例
def add(x, y):
return x + y
轉換為如下:
def add(x): #通過嵌套函數可以完成柯里化
def _add(y):
return x+y
return _add
add(5)(6)

##**裝飾器(無參)**
- 它是一個函數
- 函數作為它的形參
- 返回值也是一個函數
- 可以使用@functionname方式,簡化調用
- 裝飾器是高階函數,但裝飾器是對傳入函數的功能的裝飾(功能增強)

##舉例
 import datetime
 import time
 def logger(fn):
 def wrap(*args, **kwargs):
 # before 
 print("args={}, kwargs={}".format(args,kwargs))
 start = datetime.datetime.now()
 ret = fn(*args, **kwargs)
 # after 
 duration = datetime.datetime.now() - start
 print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
 return ret
 return wrap
 @logger #add = logger(add)
 def add(x, y):
 print("===call add===========")
 time.sleep(2)
 return x + y
 print(add(4, y=7))
 
##**文檔字符串**
- python是文檔字符串Documentation Strings
- 在函數語句塊的第一行,且習慣是多行的文本,所以多使用三引號
- 慣例是首字母大寫,第一行概述,空一行,第三行寫詳細描述
- 可以使用特殊屬性__doc__訪問這個文檔
##
 def add(x,y):
 """This is a function of addition"""
 a = x+y
 return x + y
 print("name={}\ndoc={}".format(add.__name__, add.__doc__))
 print(help(add))

##**裝飾器例子**
 def logger(fn):
 def wrapper(*args,**kwargs):
 'I am wrapper'
 print('begin')
 x = fn(*args,**kwargs)
 print('end')
 return x
 return wrapper
 @logger #add = logger(add)
 def add(x,y):
 '''This is a function for add'''
 return x + y
 print("name={}, doc={}".format(add.__name__, add.__doc__))
>上述例子有副作用,原函數對象的屬性都被替換了,而使用裝飾器,我們的需求是查看原函數的屬性。
 
##解決方法 
 def copy_properties(src, dst): 
 dst.__name__ = src.__name__
 dst.__doc__ = src.__doc__
 裝飾其中調用copy_properties(fn, wrapper)
 
##**完整解決方案**
 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 = wrapper(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)
 def add(x,y):
 '''This is a function for add'''
 return x + y
 print("name={}, doc={}".format(add.__name__, add.__doc__))
 
##**帶參裝飾器**
- 它是一個函數
- 函數作為它的形參
- 返回值是一個不帶參的裝飾器函數
- 使用@functionname(參數列表)方式調用
- 可以看做在裝飾器外層又加了一層函數

##獲取函數的執行時長,對時長超過閾值的函數記錄一下
 def logger(duration):
 def _logger(fn):
 @copy_properties(fn) # wrapper = wrapper(fn)(wrapper)
 def wrapper(*args,**kwargs):
 start = datetime.datetime.now()
 ret = fn(*args,**kwargs)
 delta = (datetime.datetime.now() - start).total_seconds()
 print('so slow') if delta > duration else print('so fast')
 return ret
 return wrapper
 return _logger
 @logger(5) # add = logger(5)(add)
 def add(x,y):
 time.sleep(3)
 return x + y
 print(add(5, 6))
 將記錄的功能提取出來,這樣就可以通過外部提供的函數來靈活的控制輸出
 def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
 def _logger(fn):
 @copy_properties(fn) # wrapper = wrapper(fn)(wrapper)
 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
 
##**functools模塊**
- functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
- 類似copy_properties功能
- wrapper包裝函數,wrapped被包裝函數
- 元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性'__module__', '__name__', '__qualname__', '__doc__', '__annotations__
- 元組WRAPPER_UPDATES中要是被更新的屬性,__dict__屬性字典
- 增加一個__wrapped__屬性,保留著wrapped屬性

##**functools模塊應用**(functools.update_wrapper)
 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
 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.__wrapped__, add.__dict__, sep='\n')

##**functools模塊應用**(functools.wraps)
 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):
 time.sleep(1)
 return x + y
 print(add(5, 6), add.__name__, add.__wrapped__, add.__dict__, sep='\n')
 

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

(0)
miraclermiracler
上一篇 2017-10-23
下一篇 2017-10-23

相關推薦

  • 馬哥教育Net20第二十二天:在Centos7上實現lmnp

    要求: vhost1: pma.stuX.com, phpMyAdmin, 同時提供https服務; vhost2: wp.stuX.com, wordpress vhost3: dz.stuX.com, Discuz  環境說明: DNS是:192.168.100.7 vhos…

    Linux干貨 2016-07-02
  • yum函數介紹以及自建yum倉庫

    一、前言     在之前介紹了yum的配置(詳細請移步 http://www.www58058.com/archives/6445)。但是有沒有發現一個問題,雖然我們已將倉庫指向一個可用的倉庫服務器,但是隨著Linux的不斷升級和改版,我們是否還需要不斷的去修改倉庫的配置文件,如果只有一臺還好,那如果我們有多…

    Linux干貨 2015-07-24
  • VSFTPD+PAM+[基于文件虛擬用戶認證 | 基于MYSQL虛擬用戶認證]

    VSFTPD+PAM+[基于文件虛擬用戶認證 | 基于MYSQL虛擬用戶認證] VSFTPD+PAM+[基于文件虛擬用戶認證 | 基于MYSQL虛擬用戶認證] 一、實驗環境 二、實驗步驟 1、通過mysql數據庫方式虛擬用戶認證 1.1數據庫配置 1.2FTP配置 1.3測試 2、通過文件方式進行虛擬用戶認證 一、實驗環境 CentOS 6.7+vsftpd…

    Linux干貨 2016-04-18
  • 第四周小練習

    復制/etc/skel目錄為/home/tuser1,要求/home/tuser1及其內部文件的屬組和其它用戶均沒有任何訪問權限 cp -r /etc/skel /home/tuser1 chmod -R go= /home/tuser1 編輯/etc/group文件,添加組hadoop echo&…

    Linux干貨 2016-11-29
  • centos系統自動化安裝

    本章內容 系統安裝過程配置anaconda自動化安裝系統 安裝程序 CentOS系統安裝 系統啟動流程: bootloader–>kernel(initramfs)–>rootfs–>/sbin/init anaconda: 系統安裝程序 tui: 基于圖形庫curses的文本窗口 gui:圖形窗口 安裝程序啟動過程 MBR…

    Linux干貨 2016-09-19
  • shell中$(( ))、$( )、“與${ }的區別

    shell中$(( ))、$( )、“與${ }的區別,包括其簡單用法。

    2017-09-11
欧美性久久久久