在讲解map、reduce、filter函数之前,我们先来了解一下迭代器对象(iterable object)和生成器的相关概念
1. 迭代对象(iterable object)
迭代对象是这样一个对象,它包含有一个next()方法(__next__()方法,在Python 3x中), 这个方法的目的是进行到下一个结果,而在结束一系列结果之后,抛出StopIteration错误。
当一个循环结构(比如for)调用循环对象时,它就会每次循环的时候调用next()方法,直到StopIteration出现,for循环接收到,就知道循环已经结束,停止调用next()。
假设我们有test.txt文件,里面包含以下内容:
当我们调用:
>>>f.open('test.txt')
>>>f.next()
>>>f.next()
..
不断地输入f.next(),直到最后出现StopIteration
open()返回的实际上是一个循环对象,包含有next()方法。而该next()方法每次返回的就是新的一行的内容,到达文件结尾时举出StopIteration。这样,我们相当于手工进行了循环。
自动进行的话,就是:
在这里,for结构自动调用next()方法,将该方法的返回值赋予给line。循环知道出现StopIteration的时候结束。
相对于序列,用迭代对象来控制循环的好处在于:可以不用在循环刚开始的时候,就生成所有的元素。所使用的元素在循环过程中逐次生成。这样,就节省了空间,提高了效率,并提高编程的灵活性。
iter()函数和迭代器(iterator)
从技术上来说,迭代对象和for循环调用之间还有一个中间层,就是要将迭代对象转换成迭代器(iterator)。这一转换是通过使用iter()函数实现的。但从逻辑层面上,常常可以忽略这一层,所以迭代对象和迭代器常常相互指代对方。
2. 生成器(generator)
生成器的主要目的是构成一个用户自定义的循环对象。
生成器的编写方法和函数定义类似,只是在return的地方改为yield。生成器中可以有多个yield。当生成器遇到一个yield时,会暂停运行生成器,返回yield后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。生成器自身又构成一个迭代器,每次迭代时使用一个yield返回的值。
该生成器共有三个yield, 如果用作循环器时,会进行三次循环。再考虑如下一个生成器:
3.生成器表达式
当序列过长, 而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析。因为列表解析返回一个列表,而生成器表达式则返回一个生成器,节约内存,效率更高。
生成器表达式的语法和列表解析一样,只不过生成器表达式是被()括起来的,而不是[]
上述生成器又可以写成 生成器表达式(Generator Expression) :
4. filter()
filter(bool_func,seq):此函数的功能相当于过滤器,调用一个布尔函数bool_func来迭代遍历每个seq中的元素;返回一个使bool_seq返回值为true的元素的序列。
例如:
5. map()
map(func,seq):将seq中的元素依次执行func(item),将结果组成一个list返回;如果func为None,func表现为身份函数,返回一个含有每个序列中元素集合的n个元组的列表。
6. reduce()
reduce(func,seq[,init]):func为二元函数,将func作用于seq序列的元素,每次携带一对(先前的结果以及下一个序列的元素),连续的将现有的结果和下一个值做运算,然后再将得到的结果作用随后的序列元素上,最终返回一个单一值。如果初始值init给定,第一个比较会是init和第一个序列元素而不是序列的头两个元素。
lambda: 这是Python支持一种有趣的语法,它允许你快速定义单行的最小函数,类似与C语言中的宏,这些叫做lambda的函数,是从LISP借用来的,可以用在任何需要函数的地方:
7. 函数作为参数传递
函数可以作为一个对象进行参数传递。函数名(比如func)即指向该对象,不需要括号。比如说:
我们可以看到,test函数的第一个参数f就是一个函数对象。我们将func传递给f,那么test中的f()所做的实际上就是func()所实现的功能。
这样,我们就大大提高了程序的灵活性。假设我们有另一个函数取代func,就可以使用相同的test函数了。如下: