Python 函数式编程-高阶函数

文中知识点和代码示例学习自慕课网,python进阶部分(http://www.imooc.com/learn/317) 学习笔记

把函数作为参数

例1:求两个数字绝对值的和(abs(x)+abs(y))

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

# add 函数的x,y,f 参数可以是任何值,如果 f 是个函数,则 x,y 两个参数分别带入到 f 函数中求值后,再做为 add 函数的参数传入。
print add(-5,9,abs)

例2:求√x + √y的值

import math

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

print add(25, 9, math.sqrt)

map() 函数

它接收一个函数f和一个list,并通过把函数f依次作用在list的每个元素上,得到一个新的list 并返回。

例1:把列表总每个元素都取二次方

def f(x):
    return x * x
print map(f,[1,2,3,4,5,6,7,8,9])

#[1, 4, 9, 16, 25, 36, 49, 64, 81]

例2:将列表中的字符串都变为首字母大写

def format_name(s):
    return s.title()
print map(format_name, ['adam', 'LISA', 'barT'])

#['Adam', 'Lisa', 'Bart']

reduce() 函数

reduce()函数接收的参数和map()类似,一个函数f,一个list,但行为和map()不同,reduce()传入的函数f必须接收两个参数,reduce()list的每个元素反复调用函数f,并返回最终结果值。

例1:求一个列表中所有元素的和

def f(x,y):
    return x + y
print reduce(f,[1,2,3,4,5])

先计算头两个元素:f(1, 2),结果为3;
再把结果和第3个元素计算:f(3, 3),结果为6;
再把结果和第4个元素计算:f(6, 4),结果为10;
再把结果和第5个元素计算:f(10, 5),结果为15;
由于没有更多的元素了,计算结束,返回结果15.

#15

filter() 函数

filter()函数接收一个函数f和一个list,这个函数f的作用是对每个元素进行判断,返回TrueFalse,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list

例:删除一个list中的偶数,保留奇数

def is_odd(x):
    return x % 2 == 1
print filter(is_odd(),[1,2,3,4,5,6,6,7,8,9])

#[1, 3, 5, 7, 9]

可以用来删除一个列表中我们不需要的元素。也可以使用如下方法来完成:

[i for i in [1,2,3,4,5,6,6,7,8,9] if i % 2 ==1 ]

#[1, 3, 5, 7, 9]

sorted() 函数

Python内置的 sorted()函数可对list进行排序:

>>>sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

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

例:实现倒序排列

def reversed_cmp(x, y):
    if x > y:
        return -1
    if x < y:
        return 1
    return 0

print sorted([36, 5, 12, 9, 21], reversed_cmp)

#[36, 21, 12, 9, 5]

其他方法:

# 先排序,后反转
>>> sorted([36, 21, 12, 9, 5])[::-1]
[36, 21, 12, 9, 5]
# 使用reversed()方法,返回一个迭代器。 如果使用reverse(),是直接修改原列表,不会返回新的列表
>>> list(reversed(sorted([36, 5, 12, 9, 21])))
[36, 21, 12, 9, 5]

返回函数

Python的函数不但可以返回intstrlistdict等数据类型,还可以返回函数!

例1:定义一个函数 f(),我们让它返回一个函数 g

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

例2:写一个函数calc_prod(lst),它接收一个list,返回一个函数,返回函数可以计算参数的乘积。

def calc_prod(lst):
    def f(x,y):
        return x * y
    def chengji():
        return reduce(f,lst)
    return chengji

f = calc_prod([1, 2, 3, 4])
print f()

闭包

例如上一小节返回函数中的例2chengji内层函数引用了外层函数calc_prodlst变量。像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。

闭包的特点:返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下:

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

f1, f2, f3 = count()

#9 9 9

原因就是当count()函数返回了3个函数时,这3个函数所引用的变量i的值已经变成了3。由于f1、f2、f3并没有被调用,所以,此时他们并未计算i*i,当 f1 被调用时:

>>> f1()
9     # 因为f1现在才计算i*i,但现在i的值已经变为3

因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。因此改成如下代码:

# 法一
def count():
    fs = []
    for i in range(1, 4):
        def f(j):
        # 借助函数f来避免引用循环变量i
            def g():
                return j*j
            return g
        r = f(i)
        fs.append(r)
    return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

#1 4 9

# 法二
def count():
    fs = []
    def f(j):
        def g():
            return j*j
        return g

    for i in range(1, 4):
        r = f(i)
        fs.append(r)
    return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

#1 4 9

匿名函数 lambda

高阶函数可以接收函数做参数,有些时候,我们不需要显式地定义函数,直接传入匿名函数更方便。

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

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

匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。

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

返回函数的时候,也可以返回匿名函数:

# 使用 if..else 来实现abs函数的功能
>>> myabs = lambda x: -x if x < 0 else x 
>>> myabs(-1)
1
>>> myabs(1)
1

例:使用匿名函数简化代码

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

# 使用匿名函数
print filter(lambda x:x and len(x.strip()) > 0,['test', None, '', 'str', '  ', 'END'])

# 使用 for..in..if 语句
print [i for i in ['test', None, '', 'str', '  ', 'END'] if i and len(i.strip())>0]

偏函数

当一个函数有很多参数时,调用者就需要提供多个参数。如果减少参数个数,就可以简化调用者的负担。

比如,int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换:

>>> int('12345')
12345

int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做 N 进制的转换:

>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565

假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:

def int2(x, base=2):
    return int(x, base)
    
# 这样,我们转换二进制就非常方便了:
>>> int2('1000000')
64
>>> int2('1010101')
85

functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容