Python的生成器和迭代器

1.Python的生成器(generator)

为什么要有生成器?

通过列表生成式,我们可以直接创建一个列表。但是列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。

如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。

简单一句话:我又想要得到庞大的数据,又想让它占用空间少,那就用生成器!

(1)生成器的创建

1.方法一:列表生成式中,只需要把[] 改成(), 就创建了一个generator

比如原来的列表生成式,返回的是一个list :

L = [x * x for x in range(10)]

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

改为()后,返回的是一个generator

L = (x * x for x in range(10)])

<generator object at 0x7f674d86c650>

2.方法二:使用yield关键字创建生成器

一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。

def func():
    yield
func()

<generator object func at 0x7fc07d2a7cd0>

下面将具体讨论yield关键字的用法:

yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield位置继续执行。
yield关键字 有两个主要的函数让生成器走向下一步:

  • next() 让生成器继续往下一步走。(第一次是开始运行函数,到yield的位置返回值,然后函数暂停;后面每一个next,都是从上次yield返回值的位置开始,继续走到下一个yield返回值的位置,也就是每一次的next开始的地方是接着上一次的next停止的地方执行的)。

  • .send()能传一个值,这个值作为yield表达式整体的结果。换句话说,就是send可以强行修改上一个yield表达式值。比如函数中有一个yield赋值,a = yield 5,第一次迭代到这里会返回5,a还没有赋值。第二次迭代时,使用.send(10),那么,就是强行修改yield 5表达式的值为10,本来是None的,现在是a=10。

为了更直观的了解整个yield关键字的运行步骤,下面用两个代码示范理解。

代码1

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))

代码的输出为:

starting...
4
********************
res: None
4

1.程序开始执行以后,因为foo函数中有yield关键字,所以foo函数并不会真的执行,而是先得到一个生成器g(相当于一个对象)

2.直到调用next方法,foo函数正式开始执行,先执行foo函数中的print方法,然后进入while循环

3.程序遇到yield关键字,然后把yield想想成return,return了一个4之后,程序停止,并没有执行赋值给res操作,此时next(g)语句执行完成,所以输出的前两行(第一个是while上面的print的结果,第二个是return出的结果)是执行print(next(g))的结果,

4.程序执行print(""20),输出20个*

5.又开始执行下面的print(next(g)),这个时候和上面那个差不多,不过不同的是,这个时候是从刚才那个next程序停止的地方开始执行的,也就是要执行res的赋值操作,这时候要注意,这个时候赋值操作的右边是没有值的(因为刚才那个是return出去了,并没有给赋值操作的左边传参数),所以这个时候res赋值是None,所以接着下面的输出就是res:None,

6.程序会继续在while里执行,又一次碰到yield,这个时候同样return 出4,然后程序停止,print函数输出的4就是这次return出的4.

代码示例2 ,关于.sent()函数

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(g.send(7))

输出结果

starting...
4
********************
res: 7
4

可以看到,res的值从None变成了7。原因如下:

5.程序执行g.send(7),程序会从yield关键字那一行继续向下运行,send会把7这个值赋值给res变量

6.由于send方法中包含next()方法,所以程序会继续向下运行执行print方法,然后再次进入while循环

7.程序执行再次遇到yield关键字,yield会返回后面的值后,程序再次暂停,直到再次调用next方法或send方法。

(2)生成器的取值调用

由于带有 yield 的函数不再是一个普通函数,而是一个生成器generator。

从生成器中取值有以下两种方式:

  • 可用next()调用生成器对象来取值。next 两种方式 t.next() 或 next(t)。

  • 可用for 循环获取返回值(每执行一次,取生成器里面一个值)

(基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代)。
代码示例

def foo(a,b):
    num=a
    print("start********")
    while num <= b:
        yield num
        num += 1
  • 用next()方法获取返回值
g=foo(1,3)
print(next(g))
print(next(g))
print(next(g))

start********
1
2
3

  • 用for循环的方法获取返回值
 for i in foo(0,5):
    print(i)

start********
0
1
2
3
4
5

2.Pyhton的迭代器

在Python中,迭代器是遵循迭代协议的对象,用来表示一连串数据流。重复调用迭代器的next()方法(或将其传给内置函数 next())将逐个返回数据流中的项。当没有数据可用时则将引发 StopIteration 异常。

迭代器分为两类,这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。

  • 一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
  • 一类是 generator ,包括生成器和带 yield 的generator function。

迭代器有两个基本方法:

  • iter() 返回一个迭代器对象
  • next() 逐一返回迭代对象中的项

代码示例

list = ['A', 'B', 'C']
iters = iter(list)
print(next(iters))
print(next(iters))
print(next(iters))
print(next(iters))
# list 是长度为3的列表,使用list作为参数返回的迭代器中可迭代的项目也只有3个,当超出可迭代的范围时将引发 StopIteration 异常。
# 迭代器对象可以使用for语句进行遍历。
list = ['A', 'B', 'C']
iters = iter(list)
for i in iters:
    print(i)

总结

  • 凡是可以for循环的,都是Iterable,可迭代对象

  • 凡是可以next()的,都是Iterator,生成器

  • 集合数据类型如list,truple,dict,str,都是Itrable不是Iterator,但可以通过iter()函数获得一个Iterator对象

3.Python中处理可迭代对象的常用函数

Python中有几个处理可迭代对象的常用函数,这边总结一下:

1.map函数

map() 会根据提供的函数对指定序列做映射,返回映射后的序列的迭代器。

map(function, iterable, ...)

第一个参数 function ,第二个参数是序列。以参数序列中的每一个元素调用 function 函数,返回 function 函数映射后的新列表。

2.filter函数

filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表(返回的是一个迭代器,不是一个List)。

filter(function, iterable)

该函数接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

3.reduce函数

reduce() 函数会对参数序列中元素进行累积。

reduce(function, iterable[, initializer])

函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
比如:

>>>def add(x, y) :            # 两数相加
...     return x + y
... 
>>> reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
15
>>> reduce(lambda x, y: x+y, [1,2,3,4,5])  # 使用 lambda 匿名函数
15

4.sorted函数

sorted() 函数对所有可迭代的对象进行排序操作。

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

输入一个可迭代对象,指定排序的值,返回一个排序好的序列。

>>> students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>> sorted(students, key=lambda s: s[2])            # 按年龄排序
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
 
>>> sorted(students, key=lambda s: s[2], reverse=True)       # 按降序
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>>

5.combination函数

Python itertools模块可以创建一个迭代器,返回iterable中所有长度为r的子序列,返回的子序列中的项按输入iterable中的顺序排序。

combinations(iterable, r)

代码示例

from itertools import combinations
list1 = [1, 3, 4, 5]
list2 = list(combinations(list1, 2))
print(list2)

返回结果:
[(1, 3), (1, 4), (1, 5), (3, 4), (3, 5), (4, 5)]
posted @ 2020-09-29 15:42  亚北薯条  阅读(210)  评论(0编辑  收藏  举报