函數式編程: 更抽象, 更脫離指令(計算機), 更貼近計算(數學).
f=abs; f(-10)
abs=len
高階函數: 接收函數作為參數的函數
def add(x,y,f):
return f(x)+f(y)
add(-5, 9, abs)
map()
是 Python 內置的高階函數,它接收一個函數 f 和一個 list,並通過把函數 f 依次作用在 list 的每個元素上,得到一個新的 list 並返回。map()函數不改變原有的 list,而是返回一個新的 list。
def format_name(s): return s.title() print map(format_name, ['adam', 'LISA', 'barT'])
reduce()
函數也是Python內置的一個高階函數。reduce()函數接收的參數和 map()類似,一個函數 f,一個list,但行為和 map()不同,reduce()傳入的函數 f 必須接收兩個參數,reduce()對list的每個元素反復調用函數f,並返回最終結果值。
reduce(function, iterable[, initializer])
Apply function of two arguments cumulatively to the items of iterable, from left to right, so as to reduce the iterable to a single value. If the optional initializer is present, it is placed before the items of the iterable in the calculation, and serves as a default when the iterable is empty. If initializer is not given and iterable contains only one item, the first item is returned.
filter()函數接收一個函數 f 和一個list,這個函數 f 的作用是對每個元素進行判斷,返回 True或 False,filter()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的新list。
sorted()也是一個高階函數,它可以接收一個比較函數cmp
來實現自定義排序,比較函數的定義是,傳入兩個待比較的元素 x, y,如果 x 應該排在 y 的前面,返回 -1,如果 x 應該排在 y 的後面,返回 1。如果 x 和 y 相等,返回 0。
在函數內部定義一個函數 然後返回這個內部定義的函數.
返回函數可以把一些計算延遲執行
def calc_sum(lst): def lazy_sum(): return sum(lst) return lazy_sum
調用calc_sum()
並沒有計算出結果,而是返回函數:
>>> f = calc_sum([1, 2, 3, 4]) >>> f <function lazy_sum at 0x1037bfaa0>
對返回的函數進行調用時,才計算出結果:
>>> f() 10
函數f
內部定義的函數g
無法被外部訪問 → 可以防止其他代碼調用g
.
def calc_sum(lst): def lazy_sum(): return sum(lst) return lazy_sum
注意: 發現沒法把 lazy_sum
移到 calc_sum
的外部,因為它引用了 calc_sum 的參數 lst
。
像這種內層函數引用了外層函數的變量(參數也算變量),然後返回內層函數的情況,稱為閉包(Closure)。
閉包的特點是返回的函數還引用了外層函數的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數返回後不能變。
ex:
# 希望一次返回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(),f2()和f3()結果應該是1,4,9,但實際結果全部都是 9 ! 原因就是當count()函數返回了3個函數時,這3個函數所引用的變量 i 的值已經變成了3。函數只在執行時才去獲取外層參數i, 由於f1、f2、f3並沒有被調用,所以,此時他們並未計算 i*i,當 f1 被調用時i已經變為3...
上面的正確寫法是:
def count(): fs = [] for i in range(1, 4): def f(j=i): return j*j fs.append(f) return fs f1, f2, f3 = count() print f1(), f2(), f3()
因此,返回函數不要引用任何循環變量,或者後續會發生變化的變量。
Python中,對匿名函數提供了有限支持。
關鍵字lambda
表示匿名函數,冒號前面的 x 表示函數參數。匿名函數有個限制,就是只能有一個表達式,不寫return,返回值就是該表達式的結果。
map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]) myabs = lambda x: -x if x < 0 else x >>> myabs(-1) 1
問題: 定義了函數, 想在運行時增加函數功能同時不改動函數代碼.
ex. 希望函數調用時打印調用日志
⇒ 方法: 高階函數: 接收要修改的函數, 進行包裝後返回包裝過的新函數.
def new_f(f): def fn(x): print 'call %s()' % f.__name__ return f(x) return fn
函數new_fn
就是所謂裝飾器函數. python的@語法可以簡化裝飾器調用:
(注意: 右邊代碼, 原本未裝飾的f1函數已經被徹底隱藏了. )
優點: 極大簡化代碼.
上面例子裡面的new_fn
函數只能裝飾接收一個參數x的函數, 想要處理接收任意參數的函數 ⇒ 利用Python的 *args
和 **kw
def log(f):
def fn(*args, kw):
print 'call %s() in %s'%( f.name, time.ctime() )
return f(*args, kw)
return fn
接上面的log函數, 如果有的函數非常重要,希望打印出'[INFO] call xxx()...',有的函數不太重要,希望打印出'[DEBUG] call xxx()...',這時,log函數本身就需要傳入'INFO'或'DEBUG'這樣的參數,類似這樣:
@log('DEBUG') def my_func(): pass
把上面的定義翻譯成高階函數的調用,就是:my_func = log('DEBUG')(my_func)
再展開一下:
log_decorator = log('DEBUG') my_func = log_decorator(my_func)
相當於:
log_decorator = log('DEBUG') @log_decorator def my_func(): pass
所以,帶參數的log函數首先返回一個decorator函數,再讓這個decorator函數接收my_func並返回新函數:
def log(prefix): def log_decorator(f): def wrapper(*args, **kw): print '[%s] %s()...' % (prefix, f.__name__) return f(*args, **kw) return wrapper return log_decorator @log('DEBUG') def test(): pass print test()
這裡用到了閉包: 最裡層wrapper函數(即修飾過個函數)用到了prefix參數.
上面的decorator會修改函數名:
def f1(x): pass print f1.__name__
⇒ 輸出: f1
def log(f): def wrapper(*args, **kw): print 'call...' return f(*args, **kw) return wrapper @log def f2(x): pass print f2.__name__
⇒ 輸出: wrapper
這對於那些依賴函數名的代碼就會失效。decorator還改變了函數的__doc__
等其它屬性。如果要讓調用者看不出一個函數經過了@decorator的“改造”,就需要把原函數的一些屬性復制到新函數中:
def log(f): def wrapper(*args, **kw): print 'call...' return f(*args, **kw) wrapper.__name__ = f.__name__ wrapper.__doc__ = f.__doc__ return wrapper
這樣寫很不方便, Python內置的functools
可以用來自動化完成這個“復制”的任務:
import functools def log(f): @functools.wraps(f) def wrapper(*args, **kw): print 'call...' return f(*args, **kw) return wrapper
functools.wraps(f)
是一個裝飾器函數, 目的是為了把最後返回的函數再次裝飾(復制f的屬性進去)... 所以對於帶參數的裝飾器, 應該在最裡面返回的wrapper函數前加上@functools.wraps(f)
import time, functools def performance(unit): def perf_decorator(f): @functools.wraps(f) def wrapper(*args, **kw): print 'call %s() in %s %s'%( f.__name__, time.time(), unit ) #closure return f(*args, **kw) return wrapper return perf_decorator @performance('ms') def factorial(n): return reduce(lambda x,y: x*y, range(1, n+1)) print factorial.__name__
假設要轉換大量的二進制字符串,每次都傳入int(x, base=2)
非常麻煩,於是,我們想到,可以定義一個int2()的函數,默認把base=2傳進去:
def int2(x, base=2): return int(x, base)
functools.partial
可以把一個參數多的函數變成一個參數少的新函數,少的參數需要在創建時指定默認值,這樣,新函數調用的難度就降低了。
functools.partial(func[,*args][, **keywords])
Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords.
import functools int2 = functools.partial(int, base=2) >>> int2('1000000') 64 >>> int2('1010101') 85 sorted_ignore_case = functools.partial(sorted, key=lambda s:s.lower()) print sorted_ignore_case(['bob', 'about', 'Zoo', 'Credit'])
CentOS上源碼安裝Python3.4 http://www.linuxidc.com/Linux/2015-01/111870.htm
《Python核心編程 第二版》.(Wesley J. Chun ).[高清PDF中文版] http://www.linuxidc.com/Linux/2013-06/85425.htm
《Python開發技術詳解》.( 周偉,宗傑).[高清PDF掃描版+隨書視頻+代碼] http://www.linuxidc.com/Linux/2013-11/92693.htm
Python腳本獲取Linux系統信息 http://www.linuxidc.com/Linux/2013-08/88531.htm
在Ubuntu下用Python搭建桌面算法交易研究環境 http://www.linuxidc.com/Linux/2013-11/92534.htm
Python 語言的發展簡史 http://www.linuxidc.com/Linux/2014-09/107206.htm
Python 的詳細介紹:請點這裡
Python 的下載地址:請點這裡