python--------函数式编程【匿名 、高级、map、filter、reduce、zip、sorted函数】

@[toc]

函数式编程概述

  • 函数式编程: 函数式=编程语言定义的函数+数学意义的函数

优点:

  • 便于进行单元测试
  • 便于调试
  • 适合并行执行

特性

  • 不可变数据(不可变:不用变量保存状态,不修改变量)

非函数式:

a=1
def incr_test1():
    global a
    a+=1
    return a
print(incr_test1())

运行结果

2

函数式:

n=1
def incr_test2(n):
    return n+1
print(incr_test2(2))

运行结果

3
  • 第一类对象(函数即"变量")---------看下面高阶函数的条件
  • 尾调用优化(尾递归) 尾调用:在函数最后一步调用另外一个函数(最后一行不一定是函数的最后一步)

函数bar3在foo3内为尾调用

def bar3(n):
    return n
def foo3():
    return bar3(3)

函数bar4和bar5在foo4内均为尾调用,二者在if判断条件不同的情况下都有可能作为函数的最后一步

def bar4(n):
    return n
def bar5(n):
    return n+1
def foo4(x):
    if type(x) is str:
        return bar4(x)
    elif type(x) is int:
        return bar5(x)

函数bar6在foo5内为非尾调用

def bar6(n):
    return n
def foo5(x):
    y=bar6(x)
    return y

函数bar7在foo6内非尾调用

def bar7(n):
    return n
def foo6(x):
    return bar7(x)+1

高阶函数

下面两个条件满足其中一个即可:

  • 函数可以接收另一个函数作为参数
def add(x,y,f):
    return f(x)+f(y)
print(add(-5,6,abs)) #f=abs abs()求绝对值

运行结果

11
  • 返回值可以是函数名
def bar1():
    print('from bar1')
def foo1():
    print('from foo')
    return bar1
m=foo1() #n=bar1
m()

运行结果

from foo
from bar1
def hanle():
    print('from hanle')
    return hanle
h=hanle()
h()

运行结果

from hanle
from hanle

这个就不是高阶函数

def foo(n):
    print(n)
def bar(name):
    print('i am name is %s'%name)
foo(bar('alex')) #bar函数没有返回值,返回值不是函数名

运行结果

i am name is alex
None

匿名函数

lambda 参数列表:函数返回值表达式语句

  • 关键字lambda表示匿名函数
  • 冒号前表示参数,冒号后表示返回值
def func(x):
    return x+1
res=func(10)
print(res)

#相当于
res=lambda x:x+1
print(res(10))   #记得带参数

运行结果

11
11
res=lambda x:x+'_ab'
print(res('alex'))

运行结果

alex_ab

lambda表达式序列

  • 可以将lambda表达式作为序列(如列表、元组或字典等)元素,从而实现挑战表的功能,也就是函数的列表

序列=[(lambda 表达式1),(lambda 表达式2), ...]

调用序列中lambda表达式的方法

序列[索引](lambda 表达式的参数列表)

定义一个lambda表达式序列,第一个元素用于计算参数的平方,第二个元素用于计算参数的立方,第三个元素用于计算参数的四次方

arr=[(lambda x:x**2),(lambda x:x**3),(lambda x:x**4)]
print(arr[0](2),arr[1](2),arr[2](2))

运行结果

4 8 16

将lambda表达式作为函数的返回值

def math(o):
    if(o==1):
        return lambda x,y:x+y
    if(o==2):
        return lambda x,y:x-y
    if(o==3):
        return lambda x,y:x*y
    if(o==4):
        return lambda x,y:x/y
action=math(1) #返回加法lambda表达式
print('10+2=',action(10,2))
action=math(2) #返回减法lambda表达式
print('10-2=',action(10,2))
action=math(3) #返回乘法lambda表达式
print('10*2=',action(10,2))
action=math(4) #返回除法lambda表达式
print('10/2=',action(10,2))

运行结果

10+2= 12
10-2= 8
10*2= 20
10/2= 5.0

python函数式编程常用的函数(它们也是高阶函数)

map()函数

  • map()函数用于指定序列中的所有元素作为参数调用指定函数,并将结果构成一个新的序列返回
  • map()函数接收两个参数,一个是函数,一个是Iterable(可迭代对象),map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回
  • map()作为高阶函数,事实上它把运算规则抽象了
arr=map(lambda x:x**2,[2,4,6,8,10])
for n in arr:
    print(n)

运行结果

4
16
36
64
100
#enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中
arr=map(lambda x:x**2,[2,4,6,8,10])
for i in enumerate(arr):
    print(i)

arr=map(lambda x,y:x+y,[1,2,3,4],[5,6,7,8])  #两个序列
for m in enumerate(arr):
    print(m)

运行结果

(0, 4)
(1, 16)
(2, 36)
(3, 64)
(4, 100)


(0, 6)
(1, 8)
(2, 10)
(3, 12)
print(map(str,[1,2,3,4,5,6]))
print(list(map(str,[1,2,3,4,5,6])))  #转换成字符串

运行结果

<map object at 0x037345D0>   #内存地址
['1', '2', '3', '4', '5', '6']

reduce()函数

  • reduce把一个函数作用在一个序列上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算

==从python3.0后,reduce()函数不被集成在python内置函数中,需要使用下面的语句引用functools模块,才能调用reduce()函数==

    from functools import reduce
def func(x,y):
    return x+y
sum=reduce(func,(2,4,6,8,10))
print(sum)

运行结果

30

程序运算过程

  • (1)reduce()函数首先使用2和4参数调用func()函数,得到结果6
  • (2)使用结果6和序列第三个元素6为参数调用func()函数,得到结果12
  • (3)使用结果12和序列第四个元素8为参数调用func()函数,得到结果20
  • (4)使用结果20和序列的第五个元素10为参数调用func()函数,得到结果30

如果要把序列[1, 3, 5, 7, 9]变换成整数13579,reduce就可以派上用场

from functools import reduce
def func1(x,y):
    return x*10+y
a=reduce(func1,[1,3,5,7,9])
print(a)

运行结果

13579

filter()函数

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

在一个list中,删掉偶数,只保留奇数

def is_odd(n):
    return n%2==1
print(list(filter(is_odd,[1,2,3,4,5,6,9,10,15])))

运行结果

[1, 3, 5, 9, 15]

把一个序列中的空字符串删掉

def not_empty(s):
    return s and s.strip()
print(list(filter(not_empty,['a',' ','b',None,'c',' '])))

运行结果

['a', 'b', 'c']
people=[{'name':'alex','age':100000},
        {'name':'xiaoli','age':10000000},
        {'name':'xiaoguo','age':9000},
        {'name':'xiaowang','age':18}
        ]
print(list(filter(lambda p:p['age']<=18,people)))

运行结果

[{'name': 'xiaowang', 'age': 18}]
  • 可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数
  • 注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list

map() filter() reduce() 区别

  • map() 处理序列中的每个元素,得到的结果是一个'列表',该'列表'元素个数及位置与原来一样
  • filter()遍历序列中的每一个元素,判断每个元素得到的布尔值,如果是True则留下来
  • reduce() 处理一个序列,把序列进行合并操作

sorted()函数

排序函数,排序的核心是比较两个元素的大小,如果是数字我们可以直接比较,但如果是字符串或者是字典,直接比较数学上的大小是没有意义的,因此,比较过程必须通过函数抽象出来

print(sorted([36,5,-12,9,-21]))
#sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序
print(sorted([36,5,-12,9,-21],key=abs))

print(sorted(['bob','about','Zoo','Credit']))
#默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。

#现在,我们提出排序应该忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码大加改动,只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较
print(sorted(['bob','about','Zoo','Credit'],key=str.lower))

#反向排序
print(sorted(['bob','about','Zoo','Credit'],key=str.lower,reverse=True))

运行结果

[-21, -12, 5, 9, 36]
[5, 9, -12, -21, 36]
['Credit', 'Zoo', 'about', 'bob']
['about', 'bob', 'Credit', 'Zoo']
['Zoo', 'Credit', 'bob', 'about']
L=[('Bob',75),('Adam',92),('Bart',66),('Lisa',88)]
def by_name(t):
    return t[0]
def by_score(t):
    return t[1]
L2=sorted(L,key=by_name)
print('根据名字排序:',L2)
L2=sorted(L,key=by_score,reverse=True)
print('根据成绩排序:',L2)

运行结果

根据名字排序: [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
根据成绩排序: [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]

zip()函数

zip()函数以一系列列表作为参数,将列表中对应的元素打包成一个个元组,然后返回由这些元组组成的列表

a=[1,2,3]
b=[4,5,6]
zipped=zip(a,b)
for i in zipped:
    print(i)

运行结果

(1, 4)
(2, 5)
(3, 6)

如果传入的参数长度不等,则返回列表的长度和参数中长度最短的列表相同

a=[1,2,3]
b=[4,5,6,7]
zipped=zip(a,b)
for n in zipped:
    print(n)

运行结果

(1, 4)
(2, 5)
(3, 6)

将打包结果前面加上操作符,并以此为参数调用zip()函数,可以将打包结果解压*

a=[1,2,3]
b=[4,5,6]
zipped=zip(a,b)
unzipped=zip(*zipped)
for m in unzipped:
    print(m)

运行结果

(1, 2, 3)
(4, 5, 6)

普通函数方式与函数式编程的对比

通过学习以上的函数可以更好的看出它们的区别

以普通编程方式计算列表元素中的正数之和

list=[2,-6,11,-7,8,15,-14,-1,10,-13,18]
sum=0
for i in range(len(list)):
    if list[i]>0:
        sum+=list[i]
print(sum)

运行结果

64

以函数式编程方式实现

from functools import reduce
list=[2,-6,11,-7,8,15,-14,-1,10,-13,18]
s=filter(lambda x:x>0,list)
sum=reduce(lambda x,y:x+y,s)
print(sum)

运行结果

64

相比而言,函数式编程具有如下几个特点

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

推荐阅读更多精彩内容