生成器(Generator)和迭代器(Iterator)
迭代器介绍
字符串、列表、元组、集合、字典、range对象等,虽然是可迭代对象(Iterable),但却不是迭代器对象。可以通过iter()函数变成迭代器。
迭代器是一种支持next()操作的对象。它包含了一组元素,当执行next()操作时,返回其中一个元素。
当所有元素都被返回后,再执行next()报StopIteration异常;
filter、map、zip等内置命令返回的就是一个迭代器。
map(function, iterable, …)
filter(function, iterable)
zip(iterable, iterable)
生成器介绍
生成器继承于迭代器,迭代器继承于可迭代对象,生成器是一种特殊的迭代器;
生成器用于解决内存受限问题,生成器内部实际不存储数据,只保存数据生成的计算规则,几乎没有什么内存开销;
生成器函数中包含yield关键字,执行到此时会返回yield后面的变量,并暂停程序执行。直到下次迭代(使用next)时会从上次挂起的位置继续执行;
生成器函数调用时,不会执行函数内部的代码,会直接返回一个生成器对象。通过next或者for循环才会执行函数体内代码。
二者比较
1. 生成器用于逐个生成元素,迭代器是访问集合元素的一种方式;
2. 生成器生成的元素可以以迭代器方式访问;
创建和访问生成器代码示例
对比迭代器和生成器的属性有哪些区别
def test_generator(n):for i in range(n):yield i ** 2
# 调用生成器函数
generator1 = test_generator(3)
# dir用于获取生成器所有的方法
list1 = dir(generator1)
# 创建一个迭代器
iterator1 = iter([1, 2, 3])
# dir用于获取迭代器所有的方法
list2 = dir(iterator1)
# 通过filter获取生成器和迭代器属性的差异
print(list(filter(lambda x: x not in list1, list2)))
print(list(filter(lambda x: x not in list2, list1)))
生成器除了通过yield函数生成外,还可以通过生成器表达式来创建
from timeit import timeitgene1 = (i for i in range(10))
print(gene1) # <generator object <genexpr> at 0x7f39674207c8>code1 = "[i for i in range(1000)]"
print(timeit(code1))
code2 = "(i for i in range(1000))"
print(timeit(code2))
# 通过时间对比发现,列表推导式效率较低,生成器时间更短
迭代器或生成器的访问
# 总共3种访问方式
# 通过next(g),每调用一次next(),就会拿掉一个值(表达式中的第一个值),当已经遍历到生成器的结尾,会抛一个异常StopIteration
generator1 = (i for i in range(5))
print(next(generator1)) # 0
print(generator1.__next__()) # 1
# 通过for循环,不会抛出StopIteration异常,会遍历全部
for i in generator1:print(i) # 2 3 4
# 通过list,不会抛出StopIteration异常,会遍历全部
print(list(generator1)) # [],因为上一步for循环已经被遍历完
需要特别注意的是,迭代器/生成器元素只能遍历一次。而对于普通列表的for循环,其实内部也是对迭代器的循环,只是每次使用for循环都会生成新的迭代器。而对于一个迭代器本身,只能被遍历一次。
生成器的其他方法 close() send() throw()
# close() 可以停止生成器
g=(i for i in range(5))
print(g.__next__())
g.close()
print(g.__next__()) # StopIteration异常
# throw() 允许用生成器抛出异常
def my_gen():count = 0while True:yield countcount += 1gen = my_gen()
for i in gen:print(i)if i == 3:gen.throw(ValueError('The number is 3...'))# 输出:
# 0
# 1
# 2
# 3
# ValueError: The number is 3...
# send() 和next一样可以用来生成数据,可以往生成器内部传递数据
# 使用send之前,生成器至少是通过next去生成一次数据
def demo():for i in range(10):res = yield iprint('send传入的数据,', res)g = demo()
print('next生成的数据:', next(g))print('send生成的数据:', g.send(6))
print('send生成的数据:', g.send(7))
print('send生成的数据:', g.send(8))# 输出:
# next生成的数据: 0
# send传入的数据, 6
# send生成的数据: 1
# send传入的数据, 7
# send生成的数据: 2
# send传入的数据, 8
# send生成的数据: 3
# ===============================================================
# 另一个使用send的场景
from faker import Faker
fk=Faker(locale='zh-CN')def work():number=yieldwhile True:if number==1:number=yield fk.name()elif number==2:number=yield fk.phone_number()elif number==3:number=yield fk.address()else:yield {"name":fk.name(),"phone":fk.phone_number(),"address":fk.address()}g=work()
next(g)
print('生成的内容:',g.send(1))
print('生成的内容:',g.send(2))
print('生成的内容:',g.send(3))
参考帖子
- python迭代器与生成器的区别
- python-生成器详解
- 生成器、迭代器与可迭代对象
- Python生成器(Generator)
- Python——使用__iter__和__next__实现迭代器