Python学习4:生成器、lamda表达式及map、reduce、filter函数

时间:2022-01-15 20:08:33

在讲解map、reduce、filter函数之前,我们先来了解一下迭代器对象(iterable object)和生成器的相关概念

1. 迭代对象(iterable object)

迭代对象是这样一个对象,它包含有一个next()方法(__next__()方法,在python 3x中), 这个方法的目的是进行到下一个结果,而在结束一系列结果之后,抛出StopIteration错误

当一个循环结构(比如for)调用循环对象时,它就会每次循环的时候调用next()方法,直到StopIteration出现,for循环接收到,就知道循环已经结束,停止调用next()。

假设我们有test.txt文件,里面包含以下内容:

123
abc
456

当我们调用:

>>>f.open('test.txt')

>>>f.next()

>>>f.next()

..

不断地输入f.next(),直到最后出现StopIteration

open()返回的实际上是一个循环对象,包含有next()方法。而该next()方法每次返回的就是新的一行的内容,到达文件结尾时举出StopIteration。这样,我们相当于手工进行了循环。

自动进行的话,就是:

for line in open('test.txt'):
print line

在这里,for结构自动调用next()方法,将该方法的返回值赋予给line。循环知道出现StopIteration的时候结束。

相对于序列,用迭代对象来控制循环的好处在于:可以不用在循环刚开始的时候,就生成所有的元素。所使用的元素在循环过程中逐次生成。这样,就节省了空间,提高了效率,并提高编程的灵活性。

iter()函数和迭代器(iterator)

从技术上来说,迭代对象和for循环调用之间还有一个中间层,就是要将迭代对象转换成迭代器(iterator)。这一转换是通过使用iter()函数实现的。但从逻辑层面上,常常可以忽略这一层,所以迭代对象和迭代器常常相互指代对方。


2. 生成器(generator)


生成器的主要目的是构成一个用户自定义的循环对象。

生成器的编写方法和函数定义类似,只是在return的地方改为yield。生成器中可以有多个yield。当生成器遇到一个yield时,会暂停运行生成器,返回yield后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。生成器自身又构成一个迭代器,每次迭代时使用一个yield返回的值。

def gen():
a = 100
yield a
a = a*8
yield a
yield 1000
该生成器共有三个yield, 如果用作循环器时,会进行三次循环。
for i in gen():    print i
再考虑如下一个生成器:

def gen():
for i in range(4):
yield i


3.生成器表达式


    当序列过长, 而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析。因为列表解析返回一个列表,而生成器表达式则返回一个生成器,节约内存,效率更高。

生成器表达式的语法和列表解析一样,只不过生成器表达式是被()括起来的,而不是[]


上述生成器又可以写成 生成器表达式(Generator Expression) :

G = (x for x in range(4))

4. filter()

filter(bool_func,seq):此函数的功能相当于过滤器,调用一个布尔函数bool_func来迭代遍历每个seq中的元素;返回一个使bool_seq返回值为true的元素的序列

     例如:

>>> filter(lambda x : x%2 == 0,[1,2,3,4,5])  
[2, 4]


5. map()

map(func,seq):将seq中的元素依次执行func(item),将结果组成一个list返回;如果func为None,func表现为身份函数,返回一个含有每个序列中元素集合的n个元组的列表。

>>> map(lambda x : None,[1,2,3,4])  
[None, None, None, None]
>>> map(lambda x : x * 2,[1,2,3,4])
[2, 4, 6, 8]
>>> map(lambda x : x * 2,[1,2,3,4,[5,6,7]])
[2, 4, 6, 8, [5, 6, 7, 5, 6, 7]]
>>> map(lambda x : None,[1,2,3,4])
[None, None, None, None]
另外map也支持多个sequence,这就要求function也支持相应数量的参数输入:

>>> def add(x, y): return x+y 
>>> map(add, range(8), range(8))
[0, 2, 4, 6, 8, 10, 12, 14]

6. reduce()

reduce(func,seq[,init]):func为二元函数,将func作用于seq序列的元素,每次携带一对(先前的结果以及下一个序列的元素),连续的将现有的结果和下一个值做运算,然后再将得到的结果作用随后的序列元素上,最终返回一个单一值。如果初始值init给定,第一个比较会是init和第一个序列元素而不是序列的头两个元素。

>>> reduce(lambda x,y : x + y,[1,2,3,4])  
10
>>> reduce(lambda x,y : x + y,[1,2,3,4],10)
20


>>> def add(x,y): return x + y 
>>> reduce(add, range(1, 11))
55 (注:1+2+3+4+5+6+7+8+9+10)
>>> reduce(add, range(1, 11), 20)
75 (注:1+2+3+4+5+6+7+8+9+10+20)


lambda: 这是Python支持一种有趣的语法,它允许你快速定义单行的最小函数,类似与C语言中的宏,这些叫做lambda的函数,是从LISP借用来的,可以用在任何需要函数的地方:

>>> g = lambda x: x * 2 
>>> g(3)
6
>>> (lambda x: x * 2)(3)

6


7. 函数作为参数传递

函数可以作为一个对象进行参数传递。函数名(比如func)即指向该对象,不需要括号。比如说:

def test(f, a, b):
print 'test'
print f(a, b)

test(func, 3, 5)

我们可以看到,test函数的第一个参数f就是一个函数对象。我们将func传递给f,那么test中的f()所做的实际上就是func()所实现的功能。

这样,我们就大大提高了程序的灵活性。假设我们有另一个函数取代func,就可以使用相同的test函数了。如下:

test((lambda x,y: x**2 + y), 6, 9)