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()))