Python学习笔记2:函数式编程

来源:互联网 发布:国内大学人工智能 编辑:程序博客网 时间:2024/06/06 00:31

    在上一篇博客中,我们学习了以下内容:Python的安装环境;变量和数据类型(Python内置的基本类型);List和Tuple(顺序的集合类型);条件判断和循环(控制程序流程);Dict和Set(根据key访问的集合类型);函数(定义和调用函数);切片(如何对list进行切片);迭代(如何用for循环迭代集合类型);列表生成式(如何快速生成列表)。在本篇博客中我们将学习函数式编程。

1.相关概念

函数:function
函数式:functional,编程范式
函数式编程的特点:
(1)把计算视为函数而非指令
(2)纯函数式编程:不需要变量,没有副作用,测试简单
(3)支持高阶函数,代码简洁
Python支持的函数式编程:
(1)不是纯函数式编程:允许有变量
(2)支持高阶函数:函数也可以作为变量传入
(3)支持闭包:有了闭包就能返回函数
(4)有限度地支持匿名函数

2.高阶函数
    高阶函数是指能接收函数做参数的函数。变量可以指向函数,函数的参数可以接收变量,因此一个函数可以接收另一个函数作为参数,能接收函数作为参数的函数就是高阶函数。

(1)map()函数
map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。

def format_name(s):    return s[0].upper()+s[1:].lower()print map(format_name, ['adam', 'LISA', 'barT'])

输出结果为:[‘Adam’, ‘Lisa’, ‘Bart’]

(2)reduce()函数
reduce()函数也是Python内置的一个高阶函数。reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。
例如,编写一个f函数,接收x和y,返回x和y的和:

def f(x, y):    return x + y

调用 reduce(f, [1, 3, 5, 7, 9])时,reduce函数将做如下计算:

先计算头两个元素:f(1, 3),结果为4;再把结果和第3个元素计算:f(4, 5),结果为9;再把结果和第4个元素计算:f(9, 7),结果为16;再把结果和第5个元素计算:f(16, 9),结果为25;由于没有更多的元素了,计算结束,返回结果25。

上述计算实际上是对 list 的所有元素求和。虽然Python内置了求和函数sum(),但是,利用reduce()求和也很简单。
reduce()还可以接收第3个可选参数,作为计算的初始值。如果把初始值设为100,计算:

reduce(f, [1, 3, 5, 7, 9], 100)

结果将变为125,因为第一轮计算是:
计算初始值和第一个元素:f(100, 1),结果为101。

(3)filter()函数
filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。
利用filter(),可以完成很多有用的功能,例如,删除 None 或者空字符串:

def is_not_empty(s):    return s and len(s.strip()) > 0filter(is_not_empty, ['test', None, '', 'str', '  ', 'END'])

结果:[‘test’, ‘str’, ‘END’]
注意: s.strip(rm) 删除 s 字符串中开头、结尾处的 rm 序列的字符。当rm为空时,默认删除空白符(包括’\n’, ‘\r’, ‘\t’, ’ ‘)。

(4)sorted()函数
Python内置的 sorted()函数可对list进行排序,但 sorted()也是一个高阶函数,它可以接收一个比较函数来实现自定义排序,比较函数的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。

def cmp_ignore_case(s1, s2):    t1=s1.lower()    t2=s2.lower()    if t1 > t2:        return 1    if t1 < t2:        return -1    return 0print sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case)

输出:[‘about’, ‘bob’, ‘Credit’, ‘Zoo’]。

(5)返回函数
Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数!
例如,定义一个函数 f(),我们让它返回一个函数 g,可以这样写:

def f():    print 'call f()...'    # 定义函数g:    def g():        print 'call g()...'    # 返回函数g:    return g

仔细观察上面的函数定义,我们在函数 f 内部又定义了一个函数 g。由于函数 g 也是一个对象,函数名 g 就是指向函数 g 的变量,所以,最外层函数 f 可以返回变量 g,也就是函数 g 本身。
调用函数 f,我们会得到 f 返回的一个函数:

>>> x = f()   # 调用f()call f()...>>> x   # 变量x是f()返回的函数:<function g at 0x1037bf320>>>> x()   # x指向函数,因此可以调用call g()...   # 调用x()就是执行g()函数定义的代码

(6)闭包
这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。

# 希望一次返回3个函数,分别计算1x1,2x2,3x3:def count():    fs = []    for i in range(1, 4):        def f():             return i*i        fs.append(f)    return fsf1, f2, f3 = count()print f1(), f2(), f3()#结果为:9,9,9

问题产生的原因是:因为函数只有在调用时才去获取外层参数,若函数在定义时便可获取到i的值,则可解决问题。而默认参数正好可以完成定义时获取i值,且运行时无需参数输入的功能。

def count():    fs = []#保存函数的引用    for i in range(1, 4):        def f(m=i):#使用默认参数,在函数定义时m已经被赋值            return m * m        fs.append(f)    return fsf1, f2, f3 = count()print f1(), f2(), f3()#结果为:1,4,9

(7)匿名函数
在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算 f(x)=x2 时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:

>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])[1, 4, 9, 16, 25, 36, 49, 64, 81]

通过对比可以看出,匿名函数 lambda x: x * x 实际上就是:

def f(x):    return x * x

关键字lambda 表示匿名函数,冒号前面的 x 表示函数参数。匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。

(8)decorator装饰器
问题:定义了一个函数,想在运行时动态增加功能,但又不想改动函数本身的代码。如:

def f1(x):    return x*2def f2(x):    return x*xdef f3(x):    return x*x*x

方法一:直接修改原函数的定义。

def f1(x):    print "call f1(x)..."    return x*2def f2(x):    print "call f2(x)..."    return x*xdef f3(x):    print "call f3(x)..."    return x*x*x

该方法的缺点是需要修改原函数的定义,需要增加不能的功能时就需要多次修改原函数的定义,且对于多个函数加同一种功能时,该方法需要些大量重复的代码。

方法二:装饰器decorater,使用高阶函数,接收函数作为参数,对其包装,并返回一个新的函数。

# -*- coding:UTF-8 -*-#装饰器函数def new_fn(f):    def fn(x):        print "call"+f.__name__+"()..."        return f(x)    return fn@new_fndef f1(x):    return x*2@new_fndef f2(x):    return x*x@new_fndef f3(x):    return x*x*xprint f1(5)print f2(2)print f3(3)'''g1=new_fn(f1)print g1(5)f1=new_fn(f1)print f1(5)'''

装饰器的作用:可以极大地化简代码,避免每个函数编写重复性代码。
打印日志:@log
检测性能:@performance
数据库事务:@transaction
URL路由:@post(‘/register’)

# -*- coding:UTF-8 -*-import time#装饰器函数def new_fn(f):    def fn(x):        print "call "+f.__name__+"()..."        return f(x)    return fn@new_fndef f1(x):    return x*2@new_fndef f2(x):    return x*x@new_fndef f3(x):    return x*x*xprint f1(5)print f2(2)print f3(3)'''g1=new_fn(f1)print g1(5)f1=new_fn(f1)print f1(5)'''#解决在控制台打印中文的乱码问题#print "----------------这是分割线----------------".decode('utf-8').encode('gbk')print "----------------这是分割线----------------"#日志函数'''只能给只有一个参数的函数加日志def log(f):    def fn(x):        print "call "+f.__name__+"()..."        return f(x)    return fn'''#要让 @log 自适应任何参数定义的函数,可以利用Python的 *args 和 **kw,保证任意个数的参数总是能正常调用def log(f):    def fn(*args,**kw):        print "call "+f.__name__+"()..."        return f(*args,**kw)    return fn#性能函数def performance(f):    def fn(*args,**kw):        startTime=time.clock()        res=f(*args,**kw)        endTime=time.clock()        print "running time is %f s"%(endTime-startTime)        return res    return fn#阶乘函数@performance@logdef factorial(n):    return reduce(lambda x,y:x*y,range(1,n+1))print factorial(20)#加法函数@performance@logdef add(x,y):    return x+yprint add(1,2)输出结果为:call f1()...10call f2()...4call f3()...27----------------这是分割线----------------call factorial()...running time is 0.000052 s2432902008176640000call add()...running time is 0.000022 s3

(9)偏函数
functools.partial可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值,这样,新函数调用的难度就降低了。

import functools#sorted(iterable[, cmp[, key[, reverse]]])def cmp_ignore_case(s1,s2):    t1=s1.lower()    t2=s2.lower()    if t1 > t2:        return 1    if t1 < t2:        return -1    return 0sorted_ignore_case = functools.partial(sorted,cmp=cmp_ignore_case)sorted_ignore_case2 = functools.partial(sorted,cmp=lambda x,y:cmp(x.lower(), y.lower()))sorted_reverse = functools.partial(sorted,reverse=True)print sorted(['bob', 'about', 'Zoo', 'Credit'])print sorted_ignore_case(['bob', 'about', 'Zoo', 'Credit'])print sorted_ignore_case2(['bob', 'about', 'Zoo', 'Credit'])print sorted_reverse(['bob', 'about', 'Zoo', 'Credit'])

输出结果为:

['about', 'bob', 'Credit', 'Zoo']['about', 'bob', 'Credit', 'Zoo']['bob', 'about', 'Zoo', 'Credit']
0 0