Python學習目錄python
練習代碼git
函數式編程就是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變量,所以,任意一個函數,只要輸入是肯定的,輸出就是肯定的,這種純函數咱們稱之爲沒有反作用。而容許使用變量的程序設計語言,因爲函數內部的變量狀態不肯定,一樣的輸入,可能獲得不一樣的輸出,所以,這種函數是有反作用的。函數式編程的一個特色就是,容許把函數自己做爲參數傳入另外一個函數,還容許返回一個函數!github
Python對函數式編程提供部分支持。因爲Python容許使用變量,所以,Python不是純函數式編程語言。編程
函數是Python內建支持的一種封裝,咱們經過把大段代碼拆成函數,經過一層一層的函數調用,就能夠把複雜任務分解成簡單的任務,這種分解能夠稱之爲面向過程的程序設計。函數就是面向過程的程序設計的基本單元。網絡
而函數式編程(請注意多了一個「式」字)——Functional Programming,雖然也能夠歸結到面向過程的程序設計,但其思想更接近數學計算。閉包
咱們首先要搞明白計算機(Computer)和計算(Compute)的概念。在計算機的層次上,CPU執行的是加減乘除的指令代碼,以及各類條件判斷和跳轉指令,因此,彙編語言是最貼近計算機的語言。而計算則指數學意義上的計算,越是抽象的計算,離計算機硬件越遠。對應到編程語言,就是越低級的語言,越貼近計算機,抽象程度低,執行效率高,好比C語言;越高級的語言,越貼近計算,抽象程度高,執行效率低,好比Lisp語言。app
在python中函數名是指向函數的變量,當函數的參數也是函數的時候,這種函數咱們稱之爲高階函數。編程語言
def add(x, y, f):
return f(x) + f(y)
add(1, -6, abs)
複製代碼
map()函數接收兩個參數,一個是函數,一個是Iterable,map將傳入的函數依次做用到序列的每一個元素,並把結果做爲新的Iterator返回。函數式編程
def f(x):
return x * x
r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(r))
>>> [1, 4, 9, 16, 25, 36, 49, 64, 81]
複製代碼
reduce把一個函數做用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素作累積計算,效果以下:函數
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
複製代碼
結合map和reduce能夠整理出一個str2int的函數:
from functools import reduce
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def char2num(s):
return DIGITS[s]
def str2int(s):
return reduce(lambda x, y: x * 10 + y, map(char2num, s))
複製代碼
Python內建的
filter()
函數用於過濾序列。和
map()
相似,filter()
也接收一個函數和一個序列。和map()
不一樣的是,filter()
把傳入的函數依次做用於每一個元素,而後根據返回值是True
仍是False
決定保留仍是丟棄該元素。
例如,在一個list中,刪掉偶數,只保留奇數,能夠這麼寫:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結果: [1, 5, 9, 15]
複製代碼
對list進行排序:
>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
複製代碼
此外,sorted()
函數也是一個高階函數,它還能夠接收一個key
函數來實現自定義的排序,例如按絕對值大小排序:
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
複製代碼
字符串排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
複製代碼
忽略大小寫:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
複製代碼
要進行反向排序,沒必要改動key函數,能夠傳入第三個參數reverse=True
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
複製代碼
高階函數除了能夠接受函數做爲參數外,還能夠把函數做爲結果值返回。
咱們來實現一個可變參數的求和。一般狀況下,求和的函數是這樣定義的:
def calc_sum(*args):
ax = 0
for n in args:
ax = ax + n
return ax
複製代碼
可是,若是不須要馬上求和,而是在後面的代碼中,根據須要再計算怎麼辦?能夠不返回求和的結果,而是返回求和的函數:
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
複製代碼
當咱們調用lazy_sum()
時,返回的並非求和結果,而是求和函數:
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
複製代碼
調用函數f
時,才真正計算求和的結果
>>> f()
25
複製代碼
如上所示的例子中,咱們在函數
lazy_sum
中又定義了函數sum
,而且,內部函數sum
能夠引用外部函數lazy_sum
的參數和局部變量,當lazy_sum
返回函數sum
時,相關參數和變量都保存在返回的函數中,這種稱爲「閉包(Closure)」。
注意點:返回函數不要引用任何循環變量,或者後續會發生變化的變量。
以下所示:
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
>>> f2()
9
>>> f3()
9
複製代碼
關鍵字
lambda
表示匿名函數,冒號前面的x
表示函數參數。匿名函數有個限制,就是隻能有一個表達式,不用寫return
,返回值就是該表達式的結果。
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
複製代碼
代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator)。本質上,decorator就是一個返回函數的高階函數。
定義一個能打印日誌的decorator,能夠定義以下:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
複製代碼
觀察上面的log
,由於它是一個decorator,因此接受一個函數做爲參數,並返回一個函數。咱們要藉助Python的@語法,把decorator置於函數的定義處:
@log
def now():
print('2015-3-25')
>>> now()
call now():
2015-3-25
複製代碼
把@log
放到now()
函數的定義處,至關於執行了語句:now = log(now)
,now
變量指向了新的函數。
若是decorator自己須要傳入參數,那就須要編寫一個返回decorator的高階函數,寫出來會更復雜。好比,要自定義log的文本:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
複製代碼
這個3層嵌套的decorator用法以下:
@log('execute')
def now():
print('2015-3-25')
>>> now()
execute now():
2015-3-25
複製代碼
和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:>>> now = log('execute')(now)
有個問題,通過decorator裝飾以後的函數,它們的__name__
已經從原來的'now'
變成了'wrapper'
:
>>> now.__name__
'wrapper'
複製代碼
Python內置的functools.wraps
用來複制函數_name_屬性的:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
複製代碼
或者針對帶參數的裝飾器:
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
複製代碼
Python的
functools
模塊提供了不少有用的功能,其中一個就是偏函數(Partial function)。把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單。建立偏函數時,實際上能夠接收函數對象、
*args
和**kw
這3個參數。
好比要使用int()
函數去轉換二進制字符串,本來的寫法是:int(x, base=2)
,若是量大的話,很麻煩,因此咱們能夠這麼寫:
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
複製代碼
上面的in2 = functools.partial(int, base=2)
至關於:
kw = { 'base': 2 }
int('10010', **kw)
複製代碼