1.函数式编程(functional programming)
是一种编程方法,或者说是编程模式,它将电脑运算视为函数的计算、不需要变量因而不会产生副作用、支持高阶函数(可以接受函数作为参数)。
Python并不是纯的函数式编程,它允许有变量,支持闭包,有限的支持匿名函数。
与以前的C++不同的是python的变量是可以指向函数的,而函数名其实也是一个指向函数的变量。
可以看看下面的例子:
f = abs
f(-10)
当然我们可以想到指向函数的变量其实也可以作为函数参数,这就相当于函数作为参数,就是我们说的高阶函数。
看下面的例子:
def add(x, y, f):
return f(x) + f(y)
add(-5, 6, abs)
2.几个python内置的高阶函数
map():
接受一个函数f和一个list,list里的元素依次通过f的处理,然后生成一个新的list。
看这个例子:
def format_name(s):
return s[0].upper()+s[1:].lower()
print map(format_name, ['sam', 'LIly', 'TOM'])
输出:['Sam', 'Lily', 'Tom']
reduce():
接受一个函数f和一个list,f必须接受两个参数,取list中前两个元素用f处理,结果再作为参数和后面的元素一起用f处理,依次类推。
看下面的例子:
from functools import reduce
def add(x, y):
return x + y
reduce(add, [1, 3, 5, 7, 9])
输出:25
filter():
用于过滤序列,接受一个函数f和一个list,函数f对list中每个元素进行判断,返回True或False,过滤掉不符合条件的元素,生成由符合条件的元素组成的序列。
例如下面这个过滤偶数的例子:
def is_odd(x):
return x % 2 == 1
filter(is_odd, [1, 4, 6, 7, 9, 12, 17])
sorted():
用于对list进行排序,第二个参数可以省略也可以传入一个函数cmp,来定义排序的规则:顺序的返回-1,逆序返回1,相等返回0。第三个参数如果设为reverse=True可以在原来的基础上逆序排序。
看下面的例子:
def cmp_ignore_case(s1, s2):
if s1.upper()<s2.upper():
return -1
else:
return 1
print sorted(['bob', 'about', 'Zoo', 'Credit'], key=cmp_ignore_case, reverse=True)
结果:['Zoo', 'Credit', 'bob', 'about']
3.返回函数与闭包
返回函数:
Python可以返回函数,但有一点需要注意的:
看这个例子:
def calc_prod(lst):
def prod(x1, x2):
return x1 * x2
def c_prod():
return reduce(prod, lst)
return c_prod
f = calc_prod([1, 2, 3, 4])
print f()
输出:24
这里返回函数的时候并不会执行,再调用f()的时候才会执行。
闭包:
先简单的下个定义:内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。
但是为什么要这样子做呢?我们结合下面这个例子来理解一下:
def line_conf(a, b):
def line(x):
return ax + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))
显然如果我们只是定义一个接受a、b、x三个参数的函数也是可以的,但这样我们每次就需要传入三个参数,显然麻烦很多,特别是当a、b两个参数较少改变的时候,上面代码使用闭包的简便性就体现出来了。这就是说闭包能提高程序的复用性。
一个需要注意的点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。这个可以看看廖雪峰老师慕课网上的python进阶教程深入了解。
4.匿名函数
Python匿名函数的使用比Java的简单得多:用关键字lambda且只能有一个表达式。
看下面的例子:
print filter(lambda s:s and len(s.strip()) > 0, ['test', None, '', 'str', ' ', 'END'])
这个例子的功能是过滤空字符串,冒号前面的x是参数,后面是表达式。
5.装饰器
装饰器(Decorator)用于增强函数的功能。本质上就是一个高阶函数,它接收一个函数作为参数,然后,返回一个新函数。
我们可以用@语句简化像f = decorate(f)这样的代码。
看下面的例子:
def log(f):
def fn(x):
print 'call ' + f.__name__ + '()...'
return f(x)
return fn
@log
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)
输出:
call factorial()...
3628800
但是,我们发现log()函数只适用于一个参数的函数。为了让它适用各种参数数量,我们需要做一些修改:
def log(f):
def fn(*args, **kw):
print 'call ' + f.__name__ + '()...'
return f(*args, **kw)
return fn
然后有时,我们需要log()函数打印不同的语句,这就需要我们对装饰器传入参数。
思路是这个样子的:带参数的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()
输出:
[DEBUG] test()...
None
装饰器有时会改变函数的一些属性,比如函数名等,为了保存原有的函数信息,我们可以在参数为原函数的函数下加一句 @functools.wraps(f)。
6.偏函数
我们可以用 functools.partial() 减少原函数参数的数量,创建一个新函数。
看下面的例子:
int2 = functools.partial(int, base=2)
Int函数默认第二个参数为10,表示进制,我们也可以改变第二个参数,例子的int2表示一个二进制的类型转换函数。