python链式调用顺序_Python函数式介绍二 - 链式调用

上一篇文章中我们发现我们的代码越来越长了,而且都挤在一行,代码越长,越不易读。括号一层嵌一层,真的容易绕晕。我一直觉得代码要写给人看,一直追求代码即注释这种程度的简单。

那有什么办法来简化这个问题?答案在标题,链式调用。我们仿照C#LINQ的链式接口。直接上代码,这分代码是我自己写的,很简单。有兴趣可以自己研究。里面封装了我觉得比较重要的几个高阶函数。如果不够的话也可以简单地再封装一下。如下,

#文件名FT.py

from functools import reduce

from collections import Iterator

from itertools import chain,groupby,product

class From:

src=None

def __init__(self,src):

self.src=src

def toList(self):

return list(self.src)

def toSet(self):

return set(self.src)

def toTuple(self):

return tuple(self.src)

def getSource(self):

return self.src

def map(self,func):

return From(map(func,self.src))

def filter(self,predicate):

return From(filter(predicate,self.src))

def reduce(self,func,identity=None):

if identity is None:

return reduce(func,self.src)

else:

return reduce(func,self.src,identity)

def chain(self):

return From(chain.from_iterable(self.src))

def groupby(self,func=None):

return From(map(lambda it:(it[0],list(it[1])),groupby(self.src,func)))

def product(self,tag):

return From(product(self.src,tag))

def all(self,predicate):

return all(map(lambda it:predicate(it),self.src))

def any(self,predicate):

return any(map(lambda it:predicate(it),self.src))

def first(self,predicate=None):

if predicate is None:

if isinstance(self.src,Iterator):

return next(self.src)

return next(iter(self.src))

else :

return next(filter(predicate,self.src))

def firstOrNone(self,predicate=None):

try:

if predicate is None:

if isinstance(self.src,Iterator):

return next(self.src)

return next(iter(self.src))

else :

return next(filter(predicate,self.src))

except StopIteration:

return None

以上代码我写了一个From类,后面的代码会反反复复地用到这个类。

复习map/filter/reduce

例子1 ,过滤出列表中的偶数,再乘2

from FT import From

print(From((1,2,3,4))

.filter(lambda it:it % 2==0)

.map(lambda it:it*2).toList())

'''

结果:

[4,8]

'''

例子2,过滤出列表中地偶数,结果乘2,最后再求和。

from FT import From

print(From((1,2,3,4))

.filter(lambda it:it % 2==0)

.map(lambda it:it*2).reduce(lambda acc,it:acc+it))

#加幺元

print(From((1,2,3,4))

.filter(lambda it:it % 2==0)

.map(lambda it:it*2).reduce(lambda acc,it:acc+it))

'''

结果:

12

12

'''

对比上篇文章,有没有觉得清晰很多,清晰地看到数据流一步一步地往下一个函数流。这个也是函数式地特点之一。python本不提供这个流式接口,没关系,我们自己造一个,而且没有花几行代码。居然全部代码都能在一篇文章中显示出来,不过这个跟python自己动态语言有关,若换成静态语言的话那应该要花多不少功夫。下次找机会我用C++实现一遍。

下面我们学多几个高阶函数,丰富我们的武器库。

groupby

分组是一个很常见的需求,需要的代码其实也不少,封装成高阶函数后那方便太多了。请看下面的例子

testgroupdata=[{"id":1,"name":"wwb"},{"id":1,"name":"wxa"},{"id":1,"name":"wxb"},{"id":2,"name":"wxc"},{"id":2,"name":"wxd"}]

这个是数据集。是我随便造的,没什么特殊意义。

例子1,根据id分组,显示分组

print(From(testgroupdata).groupby(lambda it:it['id']).toList())

'''

结果:

[

(

1,

[

{

'id': 1,

'name': 'wwb'

},

{

'id': 1,

'name': 'wxa'

},

{

'id': 1,

'name': 'wxb'

}

]

),

(

2,

[

{

'id': 2,

'name': 'wxc'

},

{

'id': 2,

'name': 'wxd'

}

]

)

]

'''

当然groupby后可以自己接其他高阶函数,如下例子

例子2,根据id分组后,过滤出分组的KEY为偶数的数据,也就是id为偶数的数据

print(From(testgroupdata).groupby(lambda it:it['id']).filter(lambda it:it[0]%2==0).toList())

'''

结果:

[

(

2,

[

{

'id': 2,

'name': 'wxc'

},

{

'id': 2,

'name': 'wxd'

}

]

)

]

'''

笛卡尔积

笛卡儿积太重要了,数据库两种表连接或join都可以解释为笛卡儿积,看下面的例子。

例子1,观察执行结果

print(From((1,2,3)).product(('a','b','c')).toList())

现在我们考虑一个图书馆管理系统,先观察以下数据集

students = [

{

"name":"wwb",

"book":[1,2,5]

},

{

"name":"wxa",

"book":[1,2,3]

},

{

"name":"wxb",

"book":[2,3,4]

}

]

books = [

{

"id":1,

"name":"C++ Primer"

},

{

"id":2,

"name":"Effecitve C++"

},

{

"id":3,

"name":"语文"

},{

"id":4,

"name":"数学"

},

{

"id":5,

"name":"英语"

}

]

students是图书馆借了数的同学,一个人可以借多本书,其中book记录的是图书的id;books是图书馆里面的书。现在可以看例子了。

例子2,求学生具体借了什么书

print(From(students).map(lambda s:

{

"name":s["name"],

"book":From(s["book"]).product(books).filter(lambda it:it[0]==it[1]["id"])

.map(lambda it:it[1]["name"]).toList()

}).toList())

'''

结果:

[

{

'name': 'wwb',

'book': [

'C++ Primer',

'Effecitve C++',

'英语'

]

},

{

'name': 'wxa',

'book': [

'C++ Primer',

'Effecitve C++',

'语文'

]

},

{

'name': 'wxb',

'book': [

'Effecitve C++',

'语文',

'数学'

]

}

]

'''

first/firstOrNone

这两个函数简单,就是找出符合条件的第一条数据。我常常需要这样的需求。直接看例子

#找出名字为wwb的同学,找不到的话则会抛出异常

print(From(students).first(lambda it:it["name"]=="wwb"))

#找出名字为wxx的同学,找不到的话返回None

print(From(students).firstOrNone(lambda it:it["name"]=="wxx"))

'''

结果:

{'name': 'wwb', 'book': [1, 2, 5]}

None

'''

first当找不到满足条件的数据会抛异常,而firstOrNone则会返回None。很简单。

chain

今天解释最后一个函数,chain。这个是一个非常重要的函数。我自己经常叫它扁平,用来变平数据。如

((a,b,c),(d,e,f),(g,h,i)) => (a,b,c,d,e,f,g,h,i)经过chain后,你会发现括号少了一层,原来'尖'的数据现在变'平'了,因为少了一层括号。看例子

print(From(((1,2,3),(4,5,6),(1,2))).chain().toList())

'''

结果:

[1, 2, 3, 4, 5, 6, 1, 2]

'''

回到上一篇文章统计选修数学的同学的平均分。数据集如下

students = [

{

"name":"wwb",

"sex":"1",

"course":[

{

"name":"Math",

"score":90

},

{

"name":"English",

"score":80

}

]

},

{

"name":"wxa",

"sex":"1",

"course":[

{

"name":"Music",

"score":90

},

{

"name":"English",

"score":80

}

]

},

{

"name":"wxb",

"sex":"1",

"course":[

{

"name":"Math",

"score":92

},

{

"name":"Music",

"score":80

}

]

},

]

求选修数学同学的平均分

studentmaths = From(students).map(lambda s: From(s["course"]).map(lambda c:

{

"name":s["name"],

"sex":s["sex"],

"course":c["name"],

"score":c["score"]

}).toList()).chain().filter(lambda it:it["course"]=="Math").toList()

#先打印出来看看

print(From(studentmaths).reduce(lambda acc,s:acc+s["score"],0)/len(studentmaths))

'''

结果:

[{'score': 90, 'name': 'wwb', 'sex': '1', 'course': 'Math'}, {'score': 92, 'name': 'wxb', 'sex': '1', 'course': 'Math'}]

91.0

'''

总结

高阶函数和链式调用终于讲完了,有没有发现这些例子都好简洁,基本上多复杂的需求都只用一条链子,一直连击。而且不失可读性。高阶函数需要练习才会熟悉,像sql语句一样,既简单又复杂。预告下篇文章讲组合,就用我一年前写的玩具模板引擎为例子,200行左右。

测试代码

同样我自己把测试代码贴出来吧,当然需要和文章开篇的FT.py放在一起才可以执行。

from FT import From

print(From((1,2,3,4))

.filter(lambda it:it % 2==0)

.map(lambda it:it*2).toList())

print(From((1,2,3,4))

.filter(lambda it:it % 2==0)

.map(lambda it:it*2).reduce(lambda acc,it:acc+it))

print(From((1,2,3,4))

.filter(lambda it:it % 2==0)

.map(lambda it:it*2).reduce(lambda acc,it:acc+it,0))

testgroupdata=[{"id":1,"name":"wwb"},{"id":1,"name":"wxa"},{"id":1,"name":"wxb"},{"id":2,"name":"wxc"},{"id":2,"name":"wxd"}]

print(From(testgroupdata).groupby(lambda it:it['id']).toList())

print(From(testgroupdata).groupby(lambda it:it['id']).filter(lambda it:it[0]%2==0).toList())

#笛卡尔积

print(From((1,2,3)).product(('a','b','c')).toList())

students = [

{

"name":"wwb",

"book":[1,2,5]

},

{

"name":"wxa",

"book":[1,2,3]

},

{

"name":"wxb",

"book":[2,3,4]

}

]

books = [

{

"id":1,

"name":"C++ Primer"

},

{

"id":2,

"name":"Effecitve C++"

},

{

"id":3,

"name":"语文"

},{

"id":4,

"name":"数学"

},

{

"id":5,

"name":"英语"

}

]

print(From(students).map(lambda s:

{

"name":s["name"],

"book":From(s["book"]).product(books).filter(lambda it:it[0]==it[1]["id"])

.map(lambda it:it[1]["name"]).toList()

}).toList())

#只找一个

print(From(students).first(lambda it:it["name"]=="wwb"))

print(From(students).firstOrNone(lambda it:it["name"]=="wxx"))

#chain是一个很重要的函数

# 目的 ((a,b,c),(d,e,f),(g,h,i)) => (a,b,c,d,e,f,g,h,i),去掉一层括号

print(From(((1,2,3),(4,5,6),(1,2))).chain().toList())

#回到上次的例子

students = [

{

"name":"wwb",

"sex":"1",

"course":[

{

"name":"Math",

"score":90

},

{

"name":"English",

"score":80

}

]

},

{

"name":"wxa",

"sex":"1",

"course":[

{

"name":"Music",

"score":90

},

{

"name":"English",

"score":80

}

]

},

{

"name":"wxb",

"sex":"1",

"course":[

{

"name":"Math",

"score":92

},

{

"name":"Music",

"score":80

}

]

},

]

#求选修数学同学的平均分

print(From(students).map(lambda s: From(s["course"]).map(lambda c:

{

"name":s["name"],

"sex":s["sex"],

"course":c["name"],

"score":c["score"]

}).toList()).chain().toList())

studentmaths = From(students).map(lambda s: From(s["course"]).map(lambda c:

{

"name":s["name"],

"sex":s["sex"],

"course":c["name"],

"score":c["score"]

}).toList()).chain().filter(lambda it:it["course"]=="Math").toList()

print(studentmaths)

print(From(studentmaths).reduce(lambda acc,s:acc+s["score"],0)/len(studentmaths))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值