从dict开始说起
学python的时候,我们一定会接触到dict(字典)这个数据结构。
dict结构展示了数据间(key与value)一一对应的关系,key作为一个查询索引,是不允许有重复的,而不同key所对应的value,则允许重复值的存在。
比如说,我们定义一群boys&girls,打出整个dict,再打出girls有哪些,可以这样操作:
1 | import pprint |
打出来的效果是:
1 | {'boy': ['大碗宽面'], 'girl': ['迪丽热巴', '王鸥', '鬼鬼']} |
我们可以很直观地看到这种对应关系
映射
像dict数据结构给我们展现的一样,数据间的对应关系,我们可以统称为:映射(Mapping)
如同第一话所说,程序的本质即为输入->函数->输出。输入和输出,就是一种映射关系,而实现这种映射的规则,就是函数。在dict里面,实现映射的函数,可以简化如下:
1 | def get_value(key): |
为了让我们便捷地从一个key访问到对应的value,dict不可能把所有的key都过一遍找,那样费时费力。
取而代之,dict可以直接通过一种算法函数,称之为hash,就可以把key计算(映射)为一个数值,即value的内存地址。我们通过这个内存地址,就可以取出对应的value数据了,非常简单粗暴。
数据处理小需求
现在,让我们看这样一个需求。我们的输入是:
1 | input_list = [11, 2, 12, 15, 5, 10, 7, 14, 6, 9, 1, 3, 13, 8, 4] |
也就是1~15的乱序。现在我们想输出这样的结果:
- 如果大于10, 输出字符串——这个数字大于10
- 如果小于等于10,输出字符串——这个数字小于等于10
- 被3整除的奇数不录入
这个需求并不难,我们可以轻易地打出以下的代码:
1 | output_list = [] |
打出来的结果是:
1 | 11: 大于10! |
但是我把这个代码给我敬爱的领导看了之后,领导居然看不懂,觉得我的层次不够清晰。唔妈呀,代码都这么短了,那该怎么办呀
于是,只好改成了一个逻辑层次清晰一点的结构,顺带给排了个序:
1 | output = map( |
打出来这个结果:
1 | 1: 小于等于10! |
这回领导终于满意啦~~
分解过程,持续映射
让一个普通的数据处理过程变得层次清晰,其实是有方法的,得益于python内置的高阶函数,如果你写过js,玩转es6或者lodash,绝对章口就莱。
上一段代码中,不难发现多了3个新函数:map、filter跟sorted。我们这就一一来看:
map,就是映射啦~它的输入参数是一个函数(映射规则)以及我们的一堆输入值,只要给定函数跟相应的一堆输入值,我们就可以构建映射输出。
然而,map的却并不是返回所有映射输出结果,而是单纯的一个可迭代(Iterable)的对象,这个对象具有一个叫迭代器(Iterator)的东东可以让我们访问输出的结果。迭代器,就像我敬爱的领导懒洋洋的那样,不去理它,它啥都不干,只有调用它,它才会屁颠屁颠地把下一个map输出值给取出来。这种“懒人操作”,其实是很有必要的。如果输入数据量大,我们需要的输出数据量小的话,先把所有输出都给出来,再过一遍输出数据,那要猴年马月呀!
filter,跟map一样,输入也是一个函数跟一堆输入值,基本机制都差不多,勉强算是一种映射的形式,但输出总有差别——顾名思义,filter是过滤、筛选之意,满足条件的选择,不满足条件的抛弃。因此,输入函数(规则)的返回值,应当是True或者False
sorted,顾名思义,就“排序”之意,其输入是任意可迭代的对象,以及每个元素要拿来比较的值(key),还有是否逆序(reverse)的选项。清晰易懂。
说完这些,回到我们的需求:
- 如果大于10, 输出字符串——这个数字大于10
- 如果小于等于10,输出字符串——这个数字小于等于10
- 被3整除的奇数不录入
要解决这个需求,我们应当:
- 先排个序,好看点——
sorted(input_list)
- 筛选数字——
filter(函数:输入值->是or不是被3整除奇数-> False or True, 一堆输入值:第一步的输出)
- 根据大小输出字符串——
map(函数:输入值->大于10 | 小于10 -> 输出啥啥啥, 一堆输入值:第二步的输出)
然后串起来,就是这些啦:
1 | output = map( |
是不是很清晰?那就在以后码码的时候,多试试看这种style吧~
总结
接触编程的同学们,总会听过两个词:面向对象编程(Object Oriented Programming)与函数式编程(Functional Programming),甚至有许多人鼓吹函数式编程,贬低面向对象云云,引起一番争论。
面向对象旨在把代码世界打造成我们熟悉的物以类(class)聚的现实世界。这样,整个代码架构就更容易被人理解,可维护性就更强了。因此,面向对象,是一个相对宏观的概念。
函数式编程,顾名思义,函数即为程序的基础。正如第一话所说的那样,函数,不仅是输入到输出的映射规则,而且就像活字印刷一样,是一个可复现的过程。因此,不管多长多短、多大多小的程序,都可以看做是一个或是多个函数的繁复联系,就比如说,咱们吃火龙果之后……采用函数式编程,能够极大增强程序的条理性,因此可以说,相对于面向对象,函数式编程是一个微观的概念。
面向对象与函数式编程,是你中有我、我中有你的关系,并不存在所谓的对立面。作为一个既支持面向对象又支持函数式编程的语言,Python还是很人性化滴!第二话虽然讲述的多是函数式编程相关的概念,但实际使用时,尤其代码量增大时,就需要应用面向对象的方法与设计模式,收拾一下你的项目啦~
实践出真知,希望咱们一起进步~