高阶函数与函数式编程

欲穷千里目,更上一层楼。

—— 唐·王之涣·《登鹳雀楼》

从前面章节,我们知道 Python 函数是以对象的形式实现的,属于 一等对象 ( first-class object )。根据编程语言理论,一等对象必须满足以下条件:

  • 可在运行时创建;
  • 能赋值给变量或者某种数据结构;
  • 能作为参数传递给函数;
  • 能作为函数执行结果返回;

Python 函数同时满足这几个条件,因而也被称为 一等函数高阶函数 则是指那些以函数为参数,或者将函数作为结果返回的函数。对高阶函数稍加利用,便能玩出很多花样来。本节从一些典型的案例入手,讲解 Python 函数高级用法。合理应用函数式编程技巧,不仅能让代码更加简洁优雅,还能提高开发效率和程序质量。

函数式编程技巧最适合用在数据处理场景,接下来以成绩单计算为例,展开讲解。原始数据如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
scores = [
    {
        'name': '小雪',
        'chinese': 90,
        'math': 75,
        'english': 85,
    },
    {
        'name': '小明',
        'chinese': 70,
        'math': 95,
        'english': 80,
    },
    {
        'name': '小丽',
        'chinese': 85,
        'math': 85,
        'english': 90,
    },
    {
        'name': '小宇',
        'chinese': 85,
        'math': 95,
        'english': 90,
    },
    {
        'name': '小刚',
        'chinese': 65,
        'math': 70,
        'english': 55,
    },
    {
        'name': '小新',
        'chinese': 85,
        'math': 85,
        'english': 80,
    },
]

sorted

排序是我们再熟悉不过的场景,如果待排序元素可以直接比较,调用 sorted 函数即可:

1
2
3
>>> numbers = [2, 8, 6, 9, 7, 0, 1, 7, 0, 3]
>>> sorted(numbers)
[0, 0, 1, 2, 3, 6, 7, 7, 8, 9]

对比较复杂的数据进行排序,则需要一些额外的工作。假如语文老师想对语文成绩进行排序,改如何进行呢?

sorted 支持指定一个自定义排序函数 key ,该函数以列表元素为参数,返回一个值决定该元素的次序。由于我们需要根据语文成绩对元素进行排序,因此需要实现一个函数将语文成绩提取出来作为比较基准:

1
2
def by_chinese(item):
    return item['chinese']

现在只需要将 by_chinese 函数作为 key 参数传给 sorted 即可实现语文成绩排序:

1
2
3
4
5
6
7
8
9
>>> for item in sorted(scores, key=by_chinese):
...     print(item['name'], item['chinese'])
...
小刚 65
小明 70
小丽 85
小宇 85
小新 85
小雪 90

自定义排序函数还可以控制升降序,如果需要按分数从高到底依次排序,可以返回成绩的负数作为排序基准:

1
2
def by_chinese_desc(item):
    return -item['chinese']
1
2
3
4
5
6
7
8
9
>>> for item in sorted(scores, key=by_chinese_desc):
...     print(item['name'], item['chinese'])
...
小雪 90
小丽 85
小宇 85
小新 85
小明 70
小刚 65

当然了,通过 sorted 函数 reverse 参数控制升降序,是一个更好的编程习惯,逻辑更清晰:

1
2
3
4
5
6
7
8
9
>>> for item in sorted(scores, key=by_chinese, reverse=True):
...     print(item['name'], item['chinese'])
...
小雪 90
小丽 85
小宇 85
小新 85
小明 70
小刚 65

lambda

by_chinese 这样直接返回结果的极简函数,其实没有必要大动干戈,用 匿名函数 定义即可。Python 中的 lambda 关键字用于定义匿名函数,匿名函数只需给出参数列表以及一个表达式作为函数返回值:

这样一来,by_chinese 这个自定义排序函数,可以这样来定义:

1
by_chinese = lambda item: item['chinese']

相应地,我们实现语文成绩排序的代码编程这样子:

1
2
3
4
5
6
7
8
9
>>> for item in sorted(scores, key=lambda item: item['chinese']):
...     print(item['name'], item['chinese'])
...
小刚 65
小明 70
小丽 85
小宇 85
小新 85
小雪 90

数学老师来了,也只需要改动一点点,就能实现数学成绩排序了:

1
2
3
4
5
6
7
8
9
>>> for item in sorted(scores, key=lambda item: item['math']):
...     print(item['name'], item['math'])
...
小刚 70
小雪 75
小丽 85
小新 85
小明 95
小宇 95

函数式编程语言一般都会提供 mapfilter 以及 reduce3 个高阶函数,再复杂的数据统计处理任务都可以转换成这些算子的组合。因此,不少大数据平台,例如 Hadoop 等,都以 mapreduce 为基础算子。Python 内部也自带了这几个高阶函数,那么这几个高阶函数如何与基础算子组合,才能迸发巨大威力呢?点击“阅读原文”,获取更多详情!

到底如何才能提升我的 Python 开发水平,向更高一级的岗位迈进呢? 如果你有这些问题或者疑惑,请订阅我们的专栏 Python源码深度剖析 ,阅读更多章节:

【Python源码剖析】系列文章首发于公众号【小菜学编程】,敬请关注: