1 函數式編程簡介
函數:function
函數式:functional 一種編程范式
?
特點:
把計算視為函數而非指令
純函數式編程:不需要變量,沒有副作用,測試簡單
支持高階函數,代碼簡潔
?
Python支持的函數式編程:
不是純函數式編程:允許有變量
支持高階函數:函數也可以作為變量傳入
支持閉包:有了閉包就能返回函數
有限度的支持匿名函數
?
變量可以指向函數:
>>> f=abs
>>> f(-20)
?
函數名其實就是指向函數的變量。
>>> abs=len
>>> abs([1,2,3])
?
2 高階函數
能接受函數作為參數的函數
變量可以指向函數
函數的參數可以接受變量
一個函數可以接受另一個函數作為參數
能接受函數作為參數的函數就是高階函數
def add(x, y, f)
?? return f(x) + f(y)
add(x, y, abs)
?
3 map()函數
Python內置的高階函數,接受一個函數f和一個list,并且通過把函數f依次作用在list的每個元素上,得到一個新的list并返回。
def f(x)
??? return x*x
map(f, range(1,10))
?
4 reduce()函數
參數為一個函數f,一個list。函數f接收兩個參數,reduce()對list的每個元素反復調用函數f,并返回最后結果。
def f(x, y)
??? return x + y
reduce(f, [1,3,5,7,9])??? #25
?
5 filter()函數
參數為一個函數f,一個list。函數f的作用是對每個元素進行判斷,并返回布爾值。filter()根據判斷結果對False的元素進行過濾。
def if_odd(x):
??? return x%2==1
filter(is_odd, [1,2,3,4,5])??? #[1,3,5]
?
刪除None或者空字符串
def is_not_empty(s):
??? return s and len(s.strip())>0
filter(is_not_empty, ['test', None, ' ', 'End'])
?
strip()的作用:刪除字符串首尾的空白字符。
?
6 sorted()函數
參數為一個list,一個函數f。函數f接受兩個參數x和y,如果x應該排在前面,則返回-1;如果y應該排在前面,則返回1;如果相等則返回0。
>>> sorted([36, 5, 12, 9, 21])??? #[5, 9, 12, 21, 36]
?
def reversed_cmp(x, y)
??? if x == y
??????? return 0
??? else
??????? return -cmp(x, y)
>>> sorted([36, 5, 12, 9, 21], reversed_cmp)??? #[36, 21, 12, 9, 5]
?
7 返回函數
def f():
??? print 'call f()...'
??? def g():
??????? print 'call g()...'
??? return g
>>> x=f()??? #調用f()
call f()...
>>> x??? #變量x是f()返回的函數
<function g at 0x1037bf320>
>>> x()??? #x指向函數,因此可以調用
call g()...?
注意區分返回函數和返回值
?
延遲計算
def calc_sum(lst):
??? def lazy_sum():
??????? return sum(lst)
??? return lazy_sum
>>> f=calc_sum([1,2,3,4])??? #調用calc_sum()并沒有計算出結果,而是返回函數。
>>> f
<function lazy_sum at 0x1037bfaa0>
>>> f()??? #對返回的函數進行調用時,才計算出結果
10
?
8 閉包
在函數內部定義的函數和外部定義的函數是一樣的,只是他們無法被外部訪問。
內層函數引用外層函數的變量,然后返回內層函數的情況,稱為閉包(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、f2、f3的值都返回9
?
?
def count():
??? fs=[]
??? for i in range(1, 4)
??????? def f(j):
??????????? def g():
??????????????? return j*j;
??????????? return g;
??????? fs.append(f(i))
??? return fs
f1, f2, f3 = count()??? #此時調用f1、f2、f3的值返回1,4,9
?
?
9 匿名函數
?
關鍵字lambda表示匿名函數,冒號前面的x表示函數參數。
匿名函數的限制:只能有一個表達式,該表達式就是返回值。
>>> map(lambda x : x*x, range(1, 10))
>>> sorted([1,5,3,6], lambda x,y : -cmp(x, y))
>>> myabs=lambda x : -x if x < 0 else x??? #返回匿名函數
>>> filter(lambda s :s and len(s.strip()>0), ['test', None, ' ', 'END'])
?
?
?
10 decorator裝飾器
?
問題:定義了一個函數,想在運行時動態增加功能,有不想改動函數本身的代碼。
示例:希望對下列函數調用增加log功能,打印出函數調用:
def f1(x):
??? return x*2
def f2(x):
??? return x*x
def f3():
??? return x*x*x
?
高階函數:可以接受函數作為參數;可以返回函數;接受一個函數,對其包裝,然后返回一個新的函數。
def new_fn(f):
??? def fn(x):
??????? print 'call' + f._name_ + '()'
??? return fn
>>> g1=new_fn(f1)
>>> print g1(5)
>>> f1=new_fn(f1)
>>> print f1(5)
?
裝飾器
Python內置的@語法法就是為了簡化裝飾器的調用
@new_fn
def f1(x):
??? return x*2
?
裝飾器的作用:
極大簡化代碼,避免為每個函數編寫重復性代碼
打印日志:@log
檢測性能:@performance
數據庫事務:@transaction
URL路由:@post('/register')
?
?
編寫無參數的decorator
Python的decorator本質上就是一個高階函數,它接受一個函數作為參數,然后返回一個新函數。
使用decorator用@語法,可以避免手動編寫f=decorate(f)這樣的代碼。
要讓decorator自適應任何參數定義的函數,可以利用Python的*args和**kw,保證任意個數的參數總是能正常調用。
*args是非關鍵字參數,用于元組;**kw是關鍵字參數,用于字典。
def log(f):
??? def fn(*args, **kw):
??????? print 'call' + f._name_ + '()...'
??????? return f(*args, **kw)
??? return fn
?
例子:編寫一個@performance,可以打印出函數調用的時間。
import time
def performance(f):
??? def fn(*args, **kw):
??????? t1 = time.time()
??????? r = f(*args, **kw)
??????? t2 = time.time()
??????? print 'call %s() in %fs' %(f._name_, t2-t1)
??????? return r
??? return fn
?
@performance
def factorial(n):
??? return reduce(lambda x,y:x+y, range(1,n+1)
print factorial(10)
?
?
編寫帶參數的decorator
如果有的函數非常重要,希望打印出'[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
?
def log(prefix):
??? def log_decorator(f):
??????? def wrapper(*args, **kw)
??????????? print '[%s]%s()...' %(prefix, f._name_)
??????????? return f(*args, **kw)
??????? return warpper
??? return log_decorator
?
完善decorator
由decorator返回的函數的信息與被裝飾的函數的信息不同,可以利用Python內置的functools來將原函數的所有必要屬性一一賦值到新函數上,如__name__,__doc__等。
import functools
def log(f)
??? @functools.wrap(f)
??? def wrapper(*args, **kw)
??????? print 'call ...'
??????? return f(*args, **kw)
??? return wrapper
?
需要指出的是,無法獲得原函數的原始參數信息。
?
?
?
11 偏函數
?
當一個函數有很多參數時,調用者就需要提供多個參數;如果減少參數個數,就可以簡化調用者的負擔。
>>> int('12345')
>>> int('12345', base=8)
>>> int('12345', 16)
?
假設要轉換大量的二進制字符串,每次都傳入int(x,base==2)非常麻煩,可以定義一個int2()函數,默認把base=2傳進去。
def int2(x, base=2)
??? return int(x,base)
>>> int2('101010110')
?
functools.partial就是幫助我們創建一個偏函數的。
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('101010110')
?
例子:忽略大小寫排序。
>>> import functools
>>> sorted_ignore_case = functools.partial(sorted, cmp=lambda s1,s2:cmp(s1.upper(), s2.upper()))
?
?