原文链接:https://blog.csdn.net/weixin_44683255/article/details/111272198
迭代器与生成器的区别:
(1)生成器:
生成器本质上就是一个函数,它记住了上一次返回时在函数体中的位置。
对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。
而且记录了程序执行的上下文。
生成器不仅“记住”了它的数据状态,生成还记住了程序执行的位置。
(2)迭代器
迭代器是一种支持next()操作的对象。它包含了一组元素,当执行next()操作时,返回其中一个元素。
当所有元素都被返回后,再执行next()报异常—StopIteration
生成器一定是可迭代的,也一定是迭代器对象
(3)区别:
①生成器是生成元素的,迭代器是访问集合元素的一中方式
②迭代输出生成器的内容
③迭代器是一种支持next()操作的对象
④迭代器(iterator):其中iterator对象表示的是一个数据流,可以把它看做一个有序序列,但我们不能提前知道序列的长度,只有通过nex()函数实现需要计算的下一个数据。可以看做生成器的一个子集。
目录
前言
迭代器
生成器
二者区别
生成器的巧妙使用
生成器推导式
生成器函数优雅在什么地方
yield from 方法
后记
为什么会有这篇文章?
今天我的同事w君问我,你知道生成器和迭代器的区别吗。我一时不怎么能说清楚,这时候搜了一些文章,发现写的大都是介绍两种,而并没有过多解释区别。
经过自己一般翻阅资料及理解与探索,我觉得这篇文章是可以解释清楚两种对象的区别的。
当然在介绍区别之前,我也帮大家回忆一下迭代器与生成器的定义。
我们先来看一段代码
list1 = [1,2,3]
for i in list1:
print(i)
1
2
3
这是初学者也能一眼看懂得代码,可是这个是怎么实现的呢。
是因为在python中几乎所有的容器都有__iter__内置函数,而这些函数都会返回一个迭代器。
我们对迭代器有两点要求。或者说迭代器应该具备迭代器协议。
1.该对象需提供next方法,返回迭代器的下一项
2.如果没有下一项即迭代完成时会抛出一个StopIteration异常。
下面我们来看一段代码
iterator_1 = iter(list1)
iterator_2 = iter(list1)
next(iterator_1)
1
next(iterator_1)
2
next(iterator_1)
3
next(iterator_1)
StopIteration Traceback (most recent call last)
----> 1 next(iterator_1)
StopIteration:
我们可以看出iterator_1
中的元素已经迭代完了,当继续迭代时则会抛出StopIteration异常,这里也说明我们得迭代器中的元素智能迭代一次。
print("迭代器二", next(iterator_2))
for i in iterator_2:
print(i)
迭代器二 1
2
3
我们可以看出我们一同实例化的迭代器2不受影响,可以继续迭代。
又因为我们调用了next(iterator_2)已经迭代掉了一个元素,所以我们在for循环打印的只有2和3.
如果我们再次for循环会发现打印的则为空。
这下我们全明白了。
原来我们for循环列表字典等,并不是真正的for循环列表本身。而是for循环他们返回的一个迭代器对象,这些迭代器对象只能迭代一次。(事实上不这样实现可能会实现无限次迭代的死循环现象)
事实上除了for外,sum,min,max函数等内置对象都会使用迭代器协议访问对象。也就是说他们会先调用这些对象中的__iter__方法,如果没有的话则会去找__getitem__方法。
这些就是关于迭代器的知识,事实上我们真正使用中去构建迭代器的情况很少。大多数情况下我们只会是在类中定义__getitem__方法使元素变得可迭代,而把其他的交给迭代器协议。
我们先来看一段代码
def test_generator(n): for i in range(n): yield i ** 2 generator1 = test_generator(3) generator1 next(generator1)
0
next(generator1)
1
next(generator1)
4
next(generator1)
StopIteration Traceback (most recent call last)
in
----> 1 next(generator1)
这时候我们会疑惑了,这不就是迭代器吗,为什么又单独称他为生成器呢。
我们再来介绍一下生成器
1.生成器本身是一种特殊的迭代器,也就是说生成器就是迭代器。
2.生成器会自动实现迭代器协议,也就是说只要我们yield后,自动就生成了next对象包括StopIteration等结构。
3.生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息。对生成器函数的第二次(或第n次)调用,跳转到函数上一次挂起的位置。生成器不仅“记住”了它的数据状态,生成还记住了程序执行的位置。
1.迭代器是访问容器的一种方式,也就是说容器已经出现。我们是从已有元素拓印出一份副本,只为我们此次迭代使用。而生成器则是,而生成器则是自己生成元素的。也就是前者是从有到有的复制,而后者则是从无到有的生成。
2.在用法上生成器只需要简单函数写法,配合yield就能实现。而迭代器真正开发中很难使用到。我们可以把生成器看做,python给我们提供的特殊接口实现的迭代器。
如果到这里还是不太清楚,那我们就再研究深入一点
list1 = list(dir(generator1)) # 生成器所有的方法
list2 = list(dir(iter([1,2,3]))) # 迭代器所有的方法
list(filter(lambda x: x not in list2, list1))
我们打印出生成器中不在迭代器中的方法,也就是生成器独有的方法。
['__del__', '__name__', '__qualname__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
gi_yieldfrom、gi_running、send、close 这些都不难看出是保存状态,挂起等的方法。这也就是我们上面所说的生成器是如何实现的。
我们在来看下生成器不含有的迭代器方法,也就是迭代器独有的方法。
['__length_hint__', '__setstate__']
这两个方法第一个则是迭代器数量的,第二个则是读取pickle等特殊文件的。这两个方法可能是生成器不需要的。但是也只有这两个方法。
好,我们可以简单的做下总结了。
生成器是实现自己独有方法的迭代器,我们可以把他看成迭代器的子类
[i**2 for i in old_list] # 列表推导式
(i**2 for i in old_list) # 生成器表达式
生成器表达式只需要把方括号换成原括号即可
sum([i**2 for i in old_list])
sum(i**2 for i in old_list)
python为了更好的封装,我们在进行sum函数时候可以省略最外层的符号
sum(i for i in range(5))
sum((i for i in range(5)))
这两种方式都可行,但是我们更推荐使用更简洁的方式。
那么使用生成器的优势究竟在哪呢
sum([i for i in range(100000000000)])
sum(i for i in range(100000000000))
我们可以查看一下机器的内存,第一个列表推导式的方式大概率会内存爆掉。其实也不难理解,第二种是生成器模式。生成器是使用才迭代,只迭代一次不会存在内存中。所以内存不会一直的增高。
来看下代码
def get_even_numbers(the_list):
result_list = []
for i in range(len(the_list)):
if the_list[i] % 2 == 0:
result_list.append(i)
return result_list
def get_even_numbers_by_gen(the_list):
for i in range(len(the_list)):
if the_list[i] % 2 == 0:
yield i
很明显,使用生成器的方法。无论是可读性还是代码的优雅都要高于第一种。
def test_yield_from(*iterables):
for i in iterables:
for j in i:
yield j
list(test_yield_from([1,2,3], 'abc'))
[1, 2, 3, 'a', 'b', 'c']
我们可以看到如果我们传的是可迭代对象,二次迭代时需要这样进行二次读取。
但其实python也提供看更加优雅的写法。
def test_yield_from(*iterables):
for i in iterables:
yield from i
关于可迭代对象,迭代协议,迭代器,生成器的意义
1.让for更通用
2.节省内存,不用一次性加载全部内容
————————————————
版权声明:本文为CSDN博主「达达爱吃肉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44706915/article/details/116702292