生成器和迭代器

Posted su-sir

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生成器和迭代器相关的知识,希望对你有一定的参考价值。

生成器

  定义:在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__]

 

以上是关于生成器和迭代器的主要内容,如果未能解决你的问题,请参考以下文章

Interator和Generator

Interator和Generator

代码详解生成器迭代器

迭代器和生成器

python函数:迭代器和生成器

Python中迭代器和生成器的区别与联系