標籤:bsp 參數 完成 file 預設參數 自動產生 對象 nsa 此刻
1.概念
函數:function,是編程裡面的方法
函數式:functional,是一種編程範式
2.特點
把計算視為函數,而非指令
純函數式編程:不需要變數,沒有副作用,測試簡單
支援高階函數,代碼簡潔
3.python支援的函數式編程
不是純函數式編程:允許有變數
支援高階函數:函數可以作為變數傳入
支援閉包:有了閉包就能返回函數
有限度的支援匿名函數
4.高階函數4.1概念
能接收函數作為變數的函數,稱為高階函數
4.2原理
python中函數名其實是一個變數。
>>> abs(-10)
10
>>> abs=len
>>> abs(-10)
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
abs(-10)
TypeError: object of type ‘int‘ has no len()
>>> abs([1,2,3])
3
4.3舉例
>>> def add_ab(x,y,f):
return f(x)+f(y)
>>> add_ab(-5,9,abs)
14
5.返回函數
>>> def f():
print ‘call f()...‘
# 定義函數g:
def g():
print ‘call g()...‘
# 返回函數g:
return g
>>> x=f()
call f()...
>>> x
<function g at 0x0000000002F05A58>
>>> x()
call g()...
6.閉包6.1概念
內層函數引用了外層函數的變數(外層函數的參數也算變數),然後返回內層函數的情況,稱為閉包(Closure)。
6.2特點
返回的函數還引用了外層函數的局部變數;
另外,閉包在定義時,裡面的代碼未運行,只會在真正被調用時去運行代碼,所以運行時取的變數的值為運行時刻(非定義時刻)該變數的值!!!
所以,要正確使用閉包,就要確保引用的局部變數在函數返回後不能變。
重要:預設參數值的擷取是在定義時刻!!!
# 希望一次返回3個函數,分別計算1x1,2x2,3x3:
>>>def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
>>> f1()
9
7.匿名函數7.1舉例
lambda x: x * x
等效於
def f(x):
return x * x
7.2特點:
關鍵字lambda表示匿名函數,冒號前面的一個(多個)變數表示函數參數;
只能有一個運算式,不寫return,傳回值就是該運算式的結果。
使用匿名函數,可以不必定義函數名
返回函數的時候,也可以返回匿名函數
8.裝飾器decorator8.1背景
已經定義了一個函數,想在運行時動態增加函數功能,又不必去改變函數本身的代碼
8.1.1改變原始碼
>>> def f1(x):
... print ‘call f1()‘
... return x*2
...
>>> def f2(x):
... print ‘call f2()‘
... return x*x
...
>>> def f3(x):
... print ‘call f3()‘
... return x*x*x
8.1.2使用高階函數返回新函數
>>> def new_fn(f):
... def fn(*args,**kw):
... print ‘call %s()‘ % f.__name__
... return f(*args,**kw)
... return fn
調用方法有兩種:
8.1.3通過內建@自動產生新函數
例
列印日誌:@log
檢測效能:@performance
資料庫事務:@transaction
URL路由:@post(‘/register‘)
8.1.4裝飾器概念
Python的 decorator 本質上就是一個高階函數,它接收一個函數作為參數,然後,返回一個新函數。
8.2無參數裝飾器8.2.1格式
# 定義無參裝飾器
def deco_performance(f):
# 定義裝飾器中臨時函數
def tmp(*args,**kw):
# 執行被裝飾函數,可使用有參裝飾器所帶參數
# xxx
# 返回被裝飾函數執行結果
return result
#return f(*args,**kw)
# 返回裝飾器中臨時函數名
return tmp
8.2.2舉例
# 請編寫一個@performance,它可以列印出函數調用的時間
def performance(f):
# 要讓 @performance 自適應任何參數定義的函數,可以利用Python的
# *args 和 **kw,保證任意個數的參數總是能正常調用
def tmp(*args,**kw):
print "before call--> %s" % (time.time())
# 此刻調用被裝飾的函數
result = f(*args,**kw)
print "after call--> %s" % (time.time())
# 只需要返回函數結果,不一定必須要return f(*args,**kw)
return result
# 只是返回函數名
return tmp
# @performance使用
@performance
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
# 使用@performance裝飾過的函數的調用
print factorial(10)
8.3有參數裝飾器8.3.1格式
# 定義有參裝飾器
def performance(unit):
# 定義無參裝飾器
def deco_performance(f):
# 定義裝飾器中臨時函數
def tmp(*args,**kw):
# 執行被裝飾函數,可使用有參裝飾器所帶參數
# xxx
# 返回被裝飾函數執行結果
return result
# 返回裝飾器中臨時函數名
return tmp
# 返回無參裝飾器名
return deco_performance
8.3.2舉例
# 定義有參裝飾器
def performance(unit):
# 定義無參裝飾器
def deco_performance(f):
# 定義裝飾器中臨時函數
def tmp(*args,**kw):
# 執行被裝飾函數,可使用有參裝飾器所帶參數
print "before call time --> %f" % (time.time() if unit==‘s‘ else time.time()*1000)
result=f(*args,**kw)
print "after call time --> %f" % (time.time() if unit==‘s‘ else time.time()*1000)
print "call %s()" % (f.__name__)
# 返回被裝飾函數執行結果
return result
# 返回裝飾器中臨時函數名
return tmp
# 返回無參裝飾器名
return deco_performance
# 使用有(無)參裝飾器
@performance(‘s‘)
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
# 調用使用有(無)參裝飾器的函數
print factorial(10)
8.4裝飾器完善8.4.1問題
經過@decorator“改造”後的函數,返回的新函數函數名已經被改變,不是‘f2‘,而是@log內部定義的‘wrapper‘。這對於那些依賴函數名的代碼就會失效。decorator還改變了函數的__doc__等其它屬性。如果要讓調用者看不出一個函數經過了@decorator的“改造”,就需要把原函數的一些屬性複製到新函數中。
8.4.2完善方法
1.手動在裝飾器的臨時函數中手動添加屬性
def log(f):
def wrapper(*args, **kw):
print ‘call...‘
return f(*args, **kw)
wrapper.__name__ = f.__name__
wrapper.__doc__ = f.__doc__
return wrapper
2.使用python內建的functools裝飾器自動完成複製動作
import functools
def log(f):
@functools.wraps(f)
def wrapper(*args, **kw):
print ‘call...‘
return f(*args, **kw)
return wrapper
8.4.3無法避免的問題
最後需要指出,由於我們把原函數簽名改成了(*args, **kw),因此,無法獲得原函數的原始參數資訊。
9偏函數9.1概念
已經定義了一個函數,函數的參數個數太多,想在運行時動態減少函數必須輸入的參數,從而在調用時更簡單,實現與預設參數相同的功能,但不改變原本函數。
9.2舉例
>>> int(‘12345‘)
12345
>>> int(‘12345‘,8)
5349
>>> int2 = functools.partial(int,base=2)
>>> int2(‘1111‘)
15
9.3注意
建立偏函數時,實際上可以接收函數對象、*args和**kw這3個參數,如果傳入xx=xx類型的參數,代表傳入kw;如果傳入xx類型的參數,會作為args自動加入到參數元組中,需注意是否會影響原本函數應有結果。
>>> max(3,4,5)
5
>>> max2 = functools.partial(max,10)
>>> max2(3,4,5)
10
python進階-- 01 函數式編程