前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >这几个Python内置的高阶函数,真香

这几个Python内置的高阶函数,真香

作者头像
somenzz
发布2020-11-25 14:32:43
3400
发布2020-11-25 14:32:43
举报
文章被收录于专栏:Python七号Python七号

阅读本文大概需要 6 分钟。

奇怪,reduce去哪了?

什么是高阶函数?,一句话,就是可以接受其他函数名称作为自己参数的函数。函数式编程说的就是这个。Python中一切皆对象,函数也是一个对象,可以作为变量名称传递给其他函数调用,高阶函数就是一种特殊的函数,有 5 个内置的函数可以大大提高我们的编程效率,分别是 sorted、filter、zip、map、reduce,这里除了 zip 函数,其他都是高阶函数。它们的用武之地非常广泛,要不也不会作为内置函数了。今天分享下它们的用法,掌握之后,你一定会觉得,真香!

1、sorted 函数

函数原型:sorted(iterable, *, key=None, reverse=False)

首先,它是一个稳定的排序算法,接收一个可迭代对象,两个必须通过关键字传参的可选参数,返回一个排序后的可迭代对象。key 是用来指定按照那个信息进行比较排序的函数,比如 key = str.lower,如果不指定,则默认按照可迭代对象中的元素进行比较。

基本用法:

代码语言:javascript
复制
>>> v_list = [5,2,3,4,1]
>>> sorted(v_list)
[1, 2, 3, 4, 5]
>>> v_tuple = (5,2,3,4,1)
>>> sorted(v_tuple)
[1, 2, 3, 4, 5]
>>> v_dict = {5:'a',2:'b',3:'c',4:'d',1:'e'}
>>> sorted(v_dict)
[1, 2, 3, 4, 5]
>>>

可以看出,只要是可迭代对象,都可以使用 sorted。

进阶用法,指定关键字进行排序:

代码语言:javascript
复制
>>> v_dict = {5:'a',2:'b',3:'c',4:'d',1:'e'}
>>> sorted(v_dict,key=lambda x:v_dict[x])
[5, 2, 3, 4, 1]
>>> student_tuples = [
...     ('john', 'A', 15),
...     ('jane', 'B', 12),
...     ('dave', 'B', 10),
... ]
>>> sorted(student_tuples, key=lambda student: student[2])   # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

还可以对对象进行排序,代码如下:

代码语言:javascript
复制
class Student:
    def __init__(self, name, grade, age):
        self.name = name
        self.grade = grade
        self.age = age
    def __repr__(self):
        return repr((self.name, self.grade, self.age))

student_objects = [
    Student('john', 'A', 15),
    Student('jane', 'B', 12),
    Student('dave', 'B', 10),
]
sorted(student_objects, key=lambda student: student.age)   # sort by age

#[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

上述指定 key 的用法非常普遍,python 还提供了非常便利的访问器 operator, operator 模块有 itemgetter() 、 attrgetter() 和 methodcaller() 函数。用法也简单易学,如下:

itemgetter 指定按待排序元素指定位置的数据进行排序:

代码语言:javascript
复制
>>> student_tuples
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>> from operator import itemgetter, attrgetter
>>> sorted(student_tuples, key=itemgetter(2))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

如果要排序的是类,可以使用 attrgetter 指定按那个属性排序:

代码语言:javascript
复制
>>> class Student:
...     def __init__(self, name, grade, age):
...         self.name = name
...         self.grade = grade
...         self.age = age
...     def __repr__(self):
...         return repr((self.name, self.grade, self.age))
...
>>> student_objects = [
...     Student('john', 'A', 15),
...     Student('jane', 'B', 12),
...     Student('dave', 'B', 10),
... ]
>>> student_objects
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>> sorted(student_objects, key=attrgetter('age'))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
>>>

还可以指定多个关键字排序,比如先按照 grade 排序,再按照 age 排序:

代码语言:javascript
复制
>>> sorted(student_tuples, key=itemgetter(1,2))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
>>> sorted(student_objects, key=attrgetter('grade', 'age'))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]

排序默认使用升序,如果要降序,传入一个关键字参数 reverse = True 即可。

代码语言:javascript
复制
>>> sorted(student_tuples, key=itemgetter(2), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]

排序是稳定的:

代码语言:javascript
复制
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
>>> sorted(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]

排序算法使用 Timsort,Timsort 是一种混合稳定的排序算法,源自归并排序和插入排序,旨在较好地处理真实世界中各种各样的数据,从 2.3 版本起,Timsort 一直是 Python 的标准排序算法。它还被 Java SE7, Android platform, GNU Octave, 谷歌浏览器和 Swift 用于对非原始类型的数组排序。

2、filter 函数

函数原型:filter(function, iterable)

filter() 函数用于过滤一个可迭代对象,过滤掉不符合条件的元素,返回由符合条件元素组成的新的可迭代对象。

filter 接收两个参数,第一个为函数,第二个为可迭代对象,可迭代对象中的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新可迭代对象中。

如获取列表中的偶数:

代码语言:javascript
复制
>>> v_list = [1,2,3,4,5,6]
>>> new = filter(lambda x: x%2 ==0, v_list)
>>> list(new)
[2, 4, 6]
>>> number_list = range(-5, 5)
>>> less_than_zero = list(filter(lambda x: x < 0, number_list))
>>> less_than_zero
[-5, -4, -3, -2, -1]

filter 使用方法很简单,也很好理解,不多说。

3、zip 函数

函数原型:zip(*iterables)

提到 zip 你一定会想到压缩,不过这里表示的是一种重新组合的意思,看下面的代码就知道了:

代码语言:javascript
复制
>>> list(zip("abc","xy"))
[('a', 'x'), ('b', 'y')]
>>> list(zip("abc","xy",[1,2,3,4]))
[('a', 'x', 1), ('b', 'y', 2)]

函数接受不限数目的可迭代对象,按照个数最小的可迭代对象进行重新组合,组合的策略就是按照原有的顺序进行,第 i 个元组包含来自每个参数序列或可迭代对象的第 i 个元素。当所输入可迭代对象中最短的一个被耗尽时,迭代器将停止迭代。当只有一个可迭代对象参数时,它将返回一个单元组的迭代器。不带参数时,它将返回一个空迭代器。用代码来解释 zip 就是:

代码语言:javascript
复制
def zip(*iterables):
    # zip('ABCD', 'xy') --> Ax By
    sentinel = object()
    iterators = [iter(it) for it in iterables]
    while iterators:
        result = []
        for it in iterators:
            elem = next(it, sentinel)
            if elem is sentinel:
                return
            result.append(elem)
        yield tuple(result)

还有一种理解就是行转列,比如:

代码语言:javascript
复制
>>> x = [1, 2, 3]
>>> y = [4, 5, 6]
>>> zipped = zip(x, y)
>>> list(zipped)
[(1, 4), (2, 5), (3, 6)]

如果是二维数,使用 zip 行转列就太方便了:

代码语言:javascript
复制
>>> array = [ [1,2,3,4],[5,6,7,8],[9,10,11,12]]
>>> list(zip(*array))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

注意,zip 以最短的可迭代对象来进行组合,其他元素丢弃,整个过程并不报错,如果不希望丢弃元素,可以使用 itertools 中的 zip_longest 方法,如下:

代码语言:javascript
复制
>>> x = [1,2,3]
>>> y = [4,5]
>>> z = [6,7,8,9]
>>> list(zip(x,y,z))
[(1, 4, 6), (2, 5, 7)]
>>> from itertools import zip_longest
>>> list(zip_longest(x,y,z))
[(1, 4, 6), (2, 5, 7), (3, None, 8), (None, None, 9)]

4、map/reduce 函数

函数原型:

  • map(function, iterable, …)
  • reduce(function, iterable[, initializer])

如果你读过 Google 的那篇大名鼎鼎的论文 “MapReduce: Simplified Data Processing on Large Clusters”,你就能大概明白 map/reduce 的概念。

简单来说,map 就是分发任务,reduce 就是对结果进行汇总。Python 内置的高阶函数 map/reduce 也是这个理儿。

比如,要对列表中的每个元素执行特定的任务,如果列表元素个数是 10 个,就要调用 10 次,有了 map 一行代码搞定:

代码语言:javascript
复制
>>> def fun(x):
...     return x*x
...
>>> v_list = [1,2,3,4,5,6,7,8,9,10]
>>> map(fun,v_list)
<map object at 0x10ff240f0>
>>> list(map(fun,v_list))
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

map() 传入的第一个参数是 fun,即函数对象本身。由于 map object 是一个 Iterator,Iterator 是惰性序列,因此通过 list() 函数让它把整个序列都计算出来并返回一个 list。有人说我不用 map,写个循环也可以搞定,没错,但那样可读性就变差了,下面的代码,你能一眼看出来 把 fun(x) 作用在 list 的每一个元素并把结果生成一个新的 list:

代码语言:javascript
复制
L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9,10]:
    L.append(fun(n))
print(L)

再看 reduce 的用法。reduce 把一个函数作用在一个可迭代对象[x1, x2, x3, …]上,第一个对象的结果作为参数传递给下一次调用,因此这个函数必须接收两个参数。

初学者可以简单的理解为累加、累积、就是前一步的结果是下一步的输入,举个例子:

代码语言:javascript
复制
>>> from functool import reduce
>>> def add(x,y):
...     return x+y
...
>>> reduce(add,[1,3,5,7])
16

Python3 中 reduce 被移到了 functools,因为 Guido 先生讨厌 reduce。

当然求和运算可以直接用 Python 内建函数 sum(),没必要动用 reduce。但是如果要把序列 [1, 3, 5, 7, 9] 变换成整数 13579,reduce 就可以派上用场:

代码语言:javascript
复制
>>> from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579

这个例子本身没多大用处,但是,如果考虑到字符串 str 也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把 str 转换为 int 的函数:

代码语言:javascript
复制
from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def char2num(s):
    return DIGITS[s]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))

也就是说,即使 Python 不提供 int() 函数,完全可以自己写一个把字符串转化为整数的函数,而且只需要几行代码!

(完)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、sorted 函数
  • 2、filter 函数
  • 3、zip 函数
  • 4、map/reduce 函数
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档