生成器和迭代器

生成器

  定义:在Python中一边循环一边计算的机制,称为生成器,生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器,生成器遇到return 会抛出异常,生成器一次只能产生一个值。

  创建生成器:

  方法1:只要把一个列表生成式的[]中括号改为()小括号,就创建一个generator

# 列表生成式
lis = [x * x for x in range(10)]
print(lis)
# 生成器
generator_ex = (x * x for x in range(10))
print(generator_ex)

# 两者之间的转换
list(x ** 3 for in range(5))


结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x0000028942CF6D48>

# 生成器可以使用next()方法来调用
generator_ex = (x for x in range(10))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))
print(next(generator_ex))

结果:
0
1
2
3
4
5
6
7
8
9
Traceback (most recent call last):
  File "C:/Users/admin/PycharmProjects/test1/generator.py", line 36, in <module>
    print(next(generator_ex))
StopIteration
Process finished with exit code 1

# 生成器可以使用__next__()方法调用
generator_ts = (x for x in range(10))
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())
print(generator_ts.__next__())

结果:
0
1
2
3
4
5
6
7
8
9
Traceback (most recent call last):
  File "C:/Users/admin/PycharmProjects/test1/generator.py", line 36, in <module>
    print(generator_ts.__next__())
StopIteration

生成器还可以使用for来调用
generator_fs = (x for x in range(10))
for i in generator_fs:
    print(i)

结果:
0
1
2
3
4
5
6
7
8
9

注意:当使用next()和__next__()调用生成器时,超出范围就会报错,但使用for就不会,因为for它把异常捕捉了。

 

  方法2:使用yield实现生成器

# 生产斐波那契数列
def fib(max):
    n,a,b =0,0,1
    while n < max:
        yield b
        a,b =b,a+b
        n = n+1
    return 'done'

g = fib(6)

while True:
    try:
        x = next(g)  # 捕获了异常,也就不会再报错了
        print('generator: ',x)
    except StopIteration as e:
        print("生成器返回值:",e.value)
        break

结果:
0
1
2
3
4
5
6
7
8
9
generator:  1
generator:  1
generator:  2
generator:  3
generator:  5
generator:  8
生成器返回值: done

  方法3:可以通过yield实现在单线程的情况下实现并发运算的效果

import time
def consumer(name):
    print("%s 准备学习啦!" %name)
    while True:
       lesson = yield # 后面send传值的时候会被lesson接收,此时yield就不起作用了
 
       print("开始[%s]了,[%s]老师来讲课了!" %(lesson,name))
 
 
def producer(name):
    c = consumer('A') # 创建生成器A
    c2 = consumer('B') # 创建生成器B
    c.__next__() # 调用生成器A,出现阻塞
    c2.__next__() # 调用生成器B,出现阻塞
    print("同学们开始上课 了!")
    for i in range(10): # 在阻塞的时候下面就实现了并发
        time.sleep(1)
        print("到了两个同学!")
        c.send(i) # 激活生成器A
        c2.send(i) # 激活生成器B
 
结果:
A 准备学习啦!
B 准备学习啦!
同学们开始上课 了!
到了两个同学!
开始[0]了,[A]老师来讲课了!
开始[0]了,[B]老师来讲课了!
到了两个同学!
开始[1]了,[A]老师来讲课了!
开始[1]了,[B]老师来讲课了!
到了两个同学!
开始[2]了,[A]老师来讲课了!
开始[2]了,[B]老师来讲课了!
到了两个同学!
开始[3]了,[A]老师来讲课了!
开始[3]了,[B]老师来讲课了!
到了两个同学!
开始[4]了,[A]老师来讲课了!
开始[4]了,[B]老师来讲课了!
到了两个同学!
开始[5]了,[A]老师来讲课了!
开始[5]了,[B]老师来讲课了!
到了两个同学!
开始[6]了,[A]老师来讲课了!
开始[6]了,[B]老师来讲课了!
到了两个同学!

 

  生成器中next()和send()的区别:  

  其实next()和send()在一定意义上作用是相似的,区别就在于send可传递参数给yield表达式,这时候传递的参数就会作为yield表达式的值,而yield的参数是返回给调用者的值,也就是说send可以强行修改上一个yield表达式值。需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。结合上面的例子:先使用c.__next__(),这样后面send才不会报错,send传的值时被lesson接收了

  

迭代器

  for循环的数据类型有以下几种:

    一类是基础容器,如list,tuple,dict,set,str等

    一类是generator,包括生成器和带yield的generator function

  凡是直接作用于for 循环的对象统称为可迭代对象:Iterable

  凡是可作用于next()函数的对象都是迭代器:Iterator,它们表示一个惰性计算的序列;

  迭代对象和迭代器可以通过iter方法转换

  

  

# 判断时迭代对象还是迭代器
from collections.abc import Iterator # 迭代器
from collections.abc import Iterable # 迭代对象
print(isinstance((x for x in range(10)), Iterator))
print(isinstance([],Iterable))

结果:
True
True

迭代对象转换为迭代器
list1 = [1,2,3]
lst = iter(list1)
print(next(lst))
print(next(lst))
print(next(lst))
print(next(lst))

结果:
1
2
3
Traceback (most recent call last):
  File "C:/Users/admin/PycharmProjects/test1/generator.py", line 53, in <module>
    print(next(lst))
StopIteration

  

  迭代器和迭代对象都有__iter__方法,但迭代器有__next__方法,迭代对象却没有

>>> dir([]) # 迭代对象
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', 

'__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__',

'__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__',

'__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__',

'__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> a = iter([]) >>> dir(a) 迭代器 ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',

'__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__',

'__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']

 

posted @ 2020-03-13 00:13  海澜时见鲸  阅读(202)  评论(0)    收藏  举报