Jean's Blog

一个专注软件测试开发技术的个人博客

0%

Python 5个常用的高阶函数,3个创建迭代器的函数

高阶函数

filter(function,iterable)

过滤器,过滤掉不满足函数function的元素,重新返回一个新的迭代器。这个函数大概等价与下自定义的filter_self:

1
2
def filter_self(function, iterable):
return iter([item for item in iterable if function(item)])
  • Filter_self函数接收一个function作为参数,满足条件的元素才得以保留。

下面根据实例看下这个函数,调用filter_self,筛选出满足指定身高的学生。条件:男生身高超过1.75,女生身高超过1.65.

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
def filter_self(function, iterable):
return iter([item for item in iterable if function(item)])


class Student():
def __init__(self, name, sex, height):
self.name = name
self.sex = sex
self.height = height


def height_condition(stu):
if stu.sex == 'male':
return stu.height > 1.75
else:
return stu.height > 1.65


students = [Student('xiaoming', 'male', 1.74),
Student('xiaohong', 'female', 1.68),
Student('xiaoli', 'male', 1.80)]
students_satisfy = filter_self(height_condition, students)
for stu in students_satisfy:
print(stu.name)

执行结果:

1
2
3
4
5
/Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/filter_sample_01.py
xiaohong
xiaoli

Process finished with exit code 0

解释下 height_condition 函数,其第一个参数是可迭代对象 iterable 中的一个元素,这是值得注意的。

以上是使用自定义过滤器,下面使用python内置的filter函数,也实现一遍学生身高筛选功能。

1
2
3
4
5
6
students = [Student('xiaoming', 'male', 1.74),
Student('xiaohong', 'female', 1.68),
Student('xiaoli', 'male', 1.80)]
students_satisfy = filter(height_condition, students)
for stu in students_satisfy:
print(stu.name)

执行结果如下,与上面使用自定义过滤函数实现的结果相同。

1
2
3
4
5
/Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/filter_sample_01.py
xiaohong
xiaoli

Process finished with exit code 0

map(function,iterable,…)

map将function映射于iterable中的每一项,并返回一个新的迭代器。

如下,map函数实现每个元素加1:

1
2
3
4
5
6
>>> mylst = [1,3,2,4,1]
>>> result = map(lambda x:x+1, mylst)
>>> result
<map object at 0x107d24880>
>>> list(result)
[2, 4, 3, 5, 2]

map函数支持传入多个可迭代对象。当传入多个可迭代对象时,输出元素个数等于较短序列长度。

如下,传入两个列表,function就需要接收两个参数,取值分别对应第一、第二个列表中的元素。

找到同时满足第一个列表的元素为奇数,第二个列表对应位置的元素为偶数。

1
2
3
4
5
6
7
8
>>> xy = map(lambda x,y: x%2==1 and y%2==0, [1,3,2,4,1], [3,2,1,2])
>>> for i in xy:
... print(i)
...
False
True
False
False

map函数,还能实现向量级运算(两个数字列表相加)。

1
2
3
4
5
6
7
>>> lst1 = [1,2,3,4,5,6]
>>> lst2 = [3,4,5,6,3,2]
>>> def vector_add(x, y):
... return list(map(lambda i, j: i+j, x,y))
...
>>> vector_add(lst1, lst2)
[4, 6, 8, 10, 8, 8]

也支持,向量长度不等的加法运算:

1
2
3
4
>>> lst1 = [1,2,3,4,5,6]
>>> lst3 = [2,4]
>>> vector_add(lst1, lst3)
[3, 6]

reduce(function,iterable[,initializer])

提到map,就会想起reduce,前者生成映射关系,后者实现归约。

reduce函数位于functools模块中,使用前需要导入。reduce函数中第一个参数是函数function。function函数,参数个数必须为2,是可迭代对象iterable内的连续两项。

计算过程,从左侧到右侧,依次归约,直到最终为单个值并返回。

1
2
3
>>> from functools import reduce
>>> reduce(lambda x,y: x+y, list(range(10)))
45

reversed(seq)

重新生成一个反向迭代器,对输入的序列实现反转。

1
2
3
4
5
6
7
8
9
>>> rev = reversed([1,4,2,3,1])
>>> for i in rev:
... print(i)
...
1
3
2
4
1

sorted(iterable,*,key=None,reverse=False)

实现对序列化对象的排序。

key参数和reverse参数必须为关键字参数,都是可省略。

1
2
3
>>> a = [1,4,2,3,1]
>>> sorted(a, reverse=True)
[4, 3, 2, 1, 1]

如果可迭代对象的元素也是一个复合对象,如字典。依据为字典的键值,sorted的key函数就会被用到。

1
2
3
4
>>> a = [{'name':'xiaoming', 'age':20, 'gender':'male'}, {'name':'xiaohong', 'age':18, 'gender':'female'}, {'name':'xiaoli', 'age':19, 'gender':'male'}]
>>> b = sorted(a, key=lambda x: x['age'], reverse=False)
>>> b
[{'name': 'xiaohong', 'age': 18, 'gender': 'female'}, {'name': 'xiaoli', 'age': 19, 'gender': 'male'}, {'name': 'xiaoming', 'age': 20, 'gender': 'male'}]

排序的可视化图,如下所示:

image-20220424173407850

迭代器

iter(object[,sentinel])

返回一个严格意义上的可迭代对象,其中,参数sentinel可有可无。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> lst = [1,3,5]
>>> it = iter(lst)
>>> it
<list_iterator object at 0x107dda9a0>
>>> it.__next__()
1
>>> it.__next__()
3
>>> it.__next__()
5
>>> it.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

it迭代结束后,在__next__时,触发StopIteration异常,即迭代器已经执行到最后。

下面的for和in结合,我们都比较熟悉,iterable为可迭代对象,依次迭代输出元素。

1
2
for ele in iterable:
print(ele)

对象iterable要想支持以上这类结构,需要满足什么条件呢?只要iterable对象支持可迭代协议,即自定义了__iter__函数,便都能配合for依次迭代输出其元素。

如下列子,TestIter类实现了迭代协议,__iter__函数。

1
2
3
4
5
6
7
8
class TestIter(object):
def __init__(self):
self._lst = [1, 3, 2, 3, 4, 5]

# 支持迭代协议(即定义有__iter__()函数)
def __iter__(self):
print("__iter__ is called")
return iter(self._lst)

所以,对象t便能结合for,迭代输出元素。

1
2
3
t = TestIter()
for e in t:
print(e)

执行结果

1
2
3
4
5
6
7
8
9
10
/Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/iter_sample_01.py
__iter__ is called
1
3
2
3
4
5

Process finished with exit code 0

next(iterator,[,default])

返回可迭代对象的下一个元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> it = iter([5,3,4,1])
>>> next(it)
5
>>> next(it)
3
>>> next(it)
4
>>> next(it)
1
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

当迭代到最后一个元素1时,再执行next,就会抛出StopIteration,迭代器终止运行。

案例:定制一个递减迭代器

编写一个迭代器,通过循环语句,对某个正整数,一次递减1,直到0.

实现类Descrease,集成与Iterator对象,重写两个方法:

  • __iter__
  • __next__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from collections.abc import Iterator


class Decrease(Iterator):
def __init__(self, init):
self.init = init

def __iter__(self):
return self

def __next__(self):
while 0 < self.init:
self.init -= 1
return self.init
raise StopIteration

调用递减迭代器Decrease:

1
2
3
descend_iter = Decrease(6)
for i in descend_iter:
print(i)

执行结果:

1
2
3
4
5
6
7
8
9
/Users/lvjing/PycharmProjects/python_base_project/venv/bin/python /Users/lvjing/PycharmProjects/python_base_project/next_sample_01.py
5
4
3
2
1
0

Process finished with exit code 0

核心要点:

  • __next__名字不能变,实现定制的迭代逻辑
  • raise StopIteration:通过raise中断程序

enumerate(iterable, start=0)

enumerate是很有用的一个内置函数,尤其要用到列表索引时。

它返回可枚举对象,也是一个迭代器。

1
2
3
4
5
6
7
>>> s = ["a", "b", "c"]
>>> for i, v in enumerate(s):
... print(i, v)
...
0 a
1 b
2 c

也可以手动执行next,依次输出一个tuple。

1
2
3
4
5
6
7
8
9
10
11
>>> enum = enumerate(s)
>>> next(enum)
(0, 'a')
>>> next(enum)
(1, 'b')
>>> next(enum)
(2, 'c')
>>> next(enum)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration