1 函數式編程概述
前提:函數在 Python 中是⼀等對象
工具:built-in ⾼階函數;lambda 函數;operator 模塊;functools 模塊
模式:閉包與裝飾器
替代:⽤用 List Comprehension 可輕鬆替代 map 和 filter(reduce 替代起來⽐比較困難)
原則:No Side Effect
何爲 No Side Effect? 函數的全部功能就僅僅是返回一個新的值而已,沒有其餘行爲,尤爲是不得修改外部變量。因⽽,各個獨⽴的部分的執⾏順序能夠隨意打亂,帶來執⾏順序上的⾃自使得⼀系列新的特性得以實現:⽆鎖的併發;惰性求值;編譯器器級別的性能優化等
1.1 程序的狀態與命令式編程
程序的狀態首先包含了當前定義的所有變量
有了程序的狀態,咱們的程序才能不斷往前推動
命令式編程,就是經過不斷修改變量的值,來保存當前運⾏的狀態,來步步推動
1.2 函數式編程
經過函數來保存程序的狀態(經過函數建立新的參數和返回值來保存狀態)
函數一層層的疊加起來,每一個函數的參數或返回值表明了⼀箇中間狀態
命令式編程⾥一次變量值的修改,在函數式編程⾥變成了⼀個函數的轉換
最天然的方式:遞歸
2 一等函數
一等對象的定義:
在運⾏時建立
能賦值給變量或數據結構中的元素
能做爲參數傳給函數
能做爲函數的返回結果
Python 中,全部函數的都是一等對象,簡稱爲一等函數
2.1 高階函數
定義:接受函數爲參數,或把函數做爲返回結果的函數
2.1.1 map 映射
map() 是 Python 內置的高階函數,它接收一個函數 f 和一個可迭代對象,並經過把函數 f 依次做用在 可迭代對象 的每一個元素上,並返回一個新的可迭代對象。
def f(x):
return x * x
print('map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])):',
list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])))
show:
map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])): [1, 4, 9, 16, 25, 36, 49, 64, 81]
替代方案:
[x*x for x in range(1,10)]
def format_name(s):
s1 = s[0:1].upper() + s[1:].lower()
return s1
print("map(format_name, ['adam', 'LISA', 'barT']):",
list(map(format_name, www.mhylpt.com['adam', 'LISA', 'barT'])))
map(format_name, ['adam', 'LISA', 'barT']): ['Adam', 'Lisa', 'Bart']
替代方案:
[format_name(name) for i, name in enumerate(['adam', 'LISA', 'barT'])]
於是,列表推導能夠很好的替換 map 函數。
2.1.2 filter 過濾器
x = [(), [], {}, None, '', False, 0, True, 1, 2, -3]
x_result = filter(bool,www.michenggw.com x)
list(x_result)
[True, 1, 2, -3]
替代方案:
[i for i in x if bool(i)]
print("filter((lambda x: x>0), range(-5, 5)):",
list(filter((lambda x: x > 0), range(-5, 5))))
filter((lambda x: x>0), range(-5, 5)): [1, 2, 3, 4]
替代方案:
[x for x in range(-5, 5) if x > 0]
2.1.3 reduce 遞推
from functools import reduce
m = 2
n = 5
reduce(lambda x, y: x * y, list(range(1, n + 1)), m)
240
def multiply(a, b):
return a * b
reduce(multiply, range(1, 5))
24
2.1.4 zip 並行
print("zip( [1, 2, 3], [4, 5, 6]):", list(zip([1, 2, 3], [4, 5, 6])))
show:
zip( [1, 2, 3], [4, 5, 6]): [(1, 4), (2, 5), (3, 6)]
2.1.5 sorted 排序
>>> sorted([x * (-1) ** x for x in range(10)])
[-9, -7, -5, -3, -1, 0, 2, 4, 6, 8]
>>> sorted([x * (-1) ** x for x in range(10)], reverse=True)
[8, 6, 4, 2, 0, -1, -3, -5, -7, -9]
>>> sorted([x * (-1) ** x for x in range(10)], key=abs)
[0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
>>> sorted([x * (-1) ** x for x in range(www.mingcheng178.com)], reverse=True, key=abs)
[-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]
min 與 max 同理。
2.2 partial
functools 這貨用於高階函數:指那些做用於函數或者返回其餘函數的函數。一般狀況下,只要是能夠被當作函數調用的對象就是這個模塊的目標。
假設有以下函數:
def multiply(x, y):
return x * y
如今,咱們想返回某個數的雙倍,即:
>>> multiply(3, y=2)
6
>>> multiply(4, y=2)
8
>>> multiply(5, y=2)
10
上面的調用有點繁瑣,每次都要傳入 y=2,咱們想到能夠定義一個新的函數,把 y=2 做爲默認值,即:
def double(x, y=2):
return multiply(x, y)
如今,咱們能夠這樣調用了:
>>> double(www.gcyl158.com)
6
>>> double(4)
8
>>> double(5)
10
事實上,咱們能夠不用本身定義 double,利用 partial,咱們能夠這樣:
from functools import partial
double = partial(multiply, y=2)
partial 接收函數 multiply 做爲參數,固定 multiply 的參數 y=2,並返回一個新的函數給 double,這跟咱們本身定義 double 函數的效果是同樣的。
因此,簡單而言,partial 函數的功能就是:把一個函數的某些參數給固定住,返回一個新的函數。
須要注意的是,咱們上面是固定了 multiply 的關鍵字參數 y=2,若是直接使用:
double = partial(multiply, 2)
則 2 是賦給了 multiply 最左邊的參數 x。
from functools import partial
def subtraction(x,www.gcyL157.com y):
return x - y
f = partial(subtraction, 4) # 4 賦給了 x
>>> f(10) # 4 - 10
-6
組合高階函數:
from functools import partial
abs_sorted = partial(sorted, key=abs)
abs_sorted([x * (-1) ** x for x in range(10)])
show:
[0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
abs_reverse_sorted = partial(sorted, key=abs, reverse=True)
abs_reverse_sorted([x * (-1) ** x for x in range(10)])
show:
[-9, 8, -7, 6, -5, 4, -3, 2, -1, 0]
2.3 匿名函數
定義:使⽤用 lambda 表達式建立的函數,函數自己沒有名字
特色:只能使⽤用純表達式,不能賦值,不能使⽤用 while 和 try 等塊語句
語法: lambda [arg1 [,arg2 [,arg3]]]: expression
Expressions get a value; Statements do something
lambda & def
寫法上:
def 能夠用代碼塊,一個代碼塊包含多個語句
lambda只能⽤單行表達式,⽽表達式僅僅是單個語句中的⼀種
結果上:
def 語句必定會增長⼀個函數名稱
lambda 不會,這就下降了了變量名污染的⻛險
能用一個表達式直接放到 return 裏返回的函數均可以⽤ lambda 速寫
def multiply(a, b):
return a * b
multiply_by_lambda = lambda x,y: x * y
List + lambda 能夠獲得⾏爲列表
f_list = [lambda x: x + 1, lambda x: x ** 2, lambda x: x ** 3]
[f_list[j](10) for j in range(3)]
[11, 100, 1000]
在 AI 領域里,這種寫法經常使用於處理數據,好比按預約的⼀系列模式處理數據
下面咱們以兩個例子來結束高階函數:
例1:
L = range(6)
# 計算l中每一個元素的兩倍和平方,並將兩種組成一個列表
# lambda表達式和python函數同樣,也能夠接受函數做爲參數
def twoTimes(x):
return x * 2
def square(x):
return x**2
print([list(map(lambda x: x(i), [twoTimes, square])) for i in L])
print(list(filter(lambda x: x % 2 == 0, L)))
# 內置reduce函數,計算 L 的和
print(reduce(lambda accumValue, newValue: accumValue + newValue, L, 0))
[[0, 0], [2, 1], [4, 4], [6, 9], [8, 16], [10, 25]]
[0, 2, 4]
15
咱們依然可使用列表解析的方式替換 map & filter:
[[twoTimes(x), square(x)] for x in L]
[x for x in L if x % 2 == 0]
經過上面的例子咱們發現,使用列表推導要比 map 與 filter 簡潔且易於理解得多。
可是,咱們這裏還有一個惰性計算的坑:
f_list = [lambda x:x**i for i in range(5)]
[f_list[j](3) for j in range(5)]
[81, 81, 81, 81, 81]
你們能夠思考爲何會出現這個意想不到的結果?python