高階函數
- First Class Object
- 函數在Python中是一等公民
- 函數也是對象,可調用(callable)的對象
- 函數可以作為普通變量、參數、返回值等等
- 高階函數
- 數學概念y=g(f(x))
- 在Python中,高階函數應該滿足下列至少一個條件
- 接受一個或者多個函數作為參數
- 輸出一個函數
- 計數器
def counter(base): def inc(step=1): base += step return base return inc 分析: - 函數counter是一個高階函數,因為它返回了一個函數對象 - 上述函數的問題在于內層函數使用外層函數的本地變量,但同時有修改它,所以調用是會報錯UnboundLocalError,想要更改只需要使用關鍵字nonlocal在要改變前聲明它是外層函數的變量 - 調用應該使用counter(base)(step) - f1 =counter(5)和f1 = counter(5)是不相等的,因為上述調用是兩次調用,這樣他們的ID就是不一樣的,所以不相等
自定義sort函數
- 排序問題:仿照內建函數sorted,自行實現一個sort函數(不使用內建函數),能夠為列表元素排序
- 思路
- 內建函數sorted是返回一個新的列表,可以設置升序或降序,可以設置一個排序函數。自定義的函數也應該有類似功能
- 新建一個列表,遍歷原列表,和新列表的值依次比較決定如何插入到新列表中
- 函數實現1。判斷以下代碼是如何排序,還能優化么?
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([1,7,2,9,3,6,8,4,5]))
- 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 print(sort([1,7,2,9,3,6,8,4,5]))
- sort函數實現3。
def sort iterable,fn=lambda a,b:a>b): ret = [] for x in iterable: for i,y in enumerate(ret); if fn(i,x): #函數返回值是bool型 ret.insert(i,x) break else: ret.append(x) return ret print(sort([1,7,2,9,3,6,8,4,5]))
內建函數-高階函數
- sorted(iterable[,key][,reverse])排序
- 返回一個新列表1,對一個可迭代對象的所有元素排序,排序規則為可以定義的函數,reverse表示是否排序翻轉
- sorted(lst,key=lambda x:6-x) #返回新列表
- list.sort(key=lambda x:6-x) #就地修改
- 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 轉換如下: def add(x): def _add(y): return x+y return _add add(5)(6) 通過嵌套函數就可以吧函數轉換稱柯里化函數
裝飾器
- 需求
- 一個加法函數,想要加強它的功能,能夠輸出被調用過以及調用的參數信息
def add(x,y): return x+y 增加信息輸出功能 def add(x,y): print('call add,{}+{}'.format(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,*args,**kwargs): #這里*args和**kwargs代表可變參數 print('begin') x = fn(*args,**kwargs) #*args,**kwargs在這表示參數解構 print('end') return x print(logger(add,5,y=50)) 柯里化 def add(x,y): return x + y def logger(fn): def _logger(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x return _logger print(logger(add)(5,y=50)) #最后也可以這么寫 # add = logger(add) # print(add(x=5,y=10))
- 裝飾器語法
def logger(fn):
def _logger(*args,**kwargs):
print('begin')
x = fn(*args,**kwargs)
print('end')
return x
return _logger
@logger #等價于add= logger(add),這就是裝飾器語法
def add(x,y):
return x+y
print(45,50)
- 上面的語法是無參裝飾器
- 它是一個函數
- 函數作為他的形參
- 返回值也是一個函數
- 可以使用@functionname方式,簡化調用
- 裝飾器是高階函數,但裝飾器對傳入函數的功能的裝飾(功能增強)
文檔字符串
- Python中文檔字符串Documentation Strings
- 在函數體語句塊的第一行,且習慣是多行的文本,所以多使用三引號
- 慣例是首字母大寫,第遺憾寫概述,空遺憾,第三行寫詳細描述
- 可以是使用特殊屬性__doc__訪問這個文檔
def add(x,y):
'''This is a function of addition'''
a = x+y
return a
print('name={}\ndoc={}'.format(add.__name__,add.__doc__))
print(help(add))
使用裝飾器的副作用
- 使用裝飾器后原函數對象的屬性都被替換了,而使用裝飾器,希望查看原函數的屬性,如何解決
- 可以自己定義一個函數將被包裝函數的屬性覆蓋掉包裝函數
def copy_properties(src,dst): #自定義復制屬性的函數 dst.__name__ = src.__name__ dst.__doc__ = src.__doc__ def logger(fn): def _logger(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x copy_properties(fn,_logger) #函數調用,復制被包裝函數屬性覆蓋包裝函數屬性 return _logger @logger def add(x,y): return x+y print(45,50)
- 凡是被裝飾的函數都需要賦值這些屬性,這個函數很通用
- 可以將被復制屬性的函數構建成裝飾器函數,帶參裝飾器
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) #_logger = copy_properties(fn)(_logger) def _logger(*args,**kwargs): print('begin') x = fn(*args,**kwargs) print('end') return x return _logger @logger def add(x,y): """this is """ return x+y print(45,50)
帶參裝飾器
- 它是一個函數
- 函數作為他的形參
- 返回值是一個不帶參的裝飾器函數
- 使用@functionname(參數列表)方式調用
- 可以看作在裝飾器外層又加了一層函數
def logger(duration):
def _logger(fn):
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)
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):
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)
print('so slow') if delta >duration else print('so fast')
return ret
return wrapper
return _logger
functools模塊
- functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
- 類似copy_properties功能
- wrapper包裝函數,wrapped被包裝函數
- 元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性
- 元組WRAPPER_UPDATES中是要被更新的屬性,__dict__屬性字典
- 增加一個__wrapped__屬性,保留著wrapped函數
- functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)和上個方法用法基本相同
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88009