摘要:只要有__iter__,那么这个对象就是可迭代对象,若对象有__iter__和__next__两种方法,则这个对象为迭代器对象。
一、概念
什么是迭代?
迭代就是重复,但是每一次重复都与上一次有关联,这就是迭代。
""" 这不是迭代,这是简单的重复 """ while True: print(1) """ 这是迭代。每一次重复与上一次的值有关 """ num = 0 while True: num +=1迭代器的出现是为了解决什么问题?
""" Python迭代器是为了解决遍历数据集合的问题而来的。在Python中,可以使用for循环遍历列表、元组、字典等数据集合,但是对于大型数据集合或者需要进行复杂的数据处理时,这种方法可能会导致内存占用过大或者处理速度过慢的问题。 迭代器提供了一种延迟计算数据的方法,只有当需要使用数据时,才会进行计算并返回结果。这种方式可以有效减少内存占用,并且可以提高代码的执行效率。"""
什么是迭代器?
""" 迭代器是一个可以记住遍历位置的对象,它从集合的第一个元素开始访问,直到所有元素被访问完毕。迭代器只能向前不会后退。由__iter__、__next__实现 """
二、迭代器示例
python设计迭代器的原因:是为了寻求一种不依赖索引也能够进行迭代取值的方案,所以python给那些没有索引的数据类型都内置了一个功能,叫__iter__功能,如果我们不想或者不能依赖索引进行迭代取值,那我们就可以调用python给我们提供的这个功能就可以了,只要调用了__iter__方法,那么python就会将迭代对象转化为迭代器对象,有了这个迭代器对象,我们就可以不依赖索引进行迭代取值了,那这个迭代器对象是怎么不依赖于索引进行迭代取值呢?
d = {"key1": 1, "key2": 2, "key3": 3} res = d.__iter__() # 将可迭代对象转化为迭代器 print(res) print(res.__next__()) # key1 print(res.__next__()) # key2 print(res.__next__()) # key3 print(res.__next__()) # 这时取值已经取完,将抛出StopIteration异常
"""
迭代器节约内存资源,把迭代器比喻成一只老母鸡,这里只能下三只蛋,下完后就死了,不能重新调用生成,如果想再次生成,就自己再新建一只老母鸡。就是说如果这个老母鸡一辈子可以生1000个鸡蛋,肯定不是它肚子里直接就存着1000个鸡蛋,而是一个一个来的,我想说的是节约内存资源,效率高
"""
while第一次循环就已经取完了老母鸡里的鸡蛋,然后老母鸡就死了,所以第二次循环是取不到值的,直接捕获异常break了。那么我们就需要重新再造一只老母鸡
while True: try: print(res.__next__()) except StopIteration: break while True: try: print(res.__next__()) except StopIteration: break """ 其他的类型只要有__iter__方法的数据类型---列表、元组、字符串、集合、还有文件,不管有索引还是没有索引,都可以用这种方法进行迭代取值。 """ # 针对上面使用的while循环,直接使用for循环要简单很多(上面与for循环原理一样) d = {"key1": 1, "key2": 2, "key3": 3} for key in d: print(key)迭代器调用iter()方法:
print(res.__iter__()) # res是迭代器,打印迭代器本身 """ 即然调用iter()打印的是迭代器本身,那么这种方式没有意义,为什么要设计出来呢?真的就是没有意义吗?不,这种方式的出现是 为了让for循环的工作原理统一起来,不管for循环的in后面跟的是可迭代对象,还是迭代器对象,都可以采用同一套运行机制,如果我们for i in 一个可迭代对象,它会去调用这个可迭代对象的__iter__方法,把它转换成一个迭代器,如果这里本身放的就是一个迭代器对象,那我for循环的工作原理不变,还是会 去调用它的__iter__(迭代器对象的__iter__),拿到的结果也是一个迭代器 """注意点:
列表、元组、集合、字典都没有__next__方法,所以他们是可迭代对象,文件对象有__iter__、__next__,是迭代器对象
三、for循环原理
1.调用__iter__()方法将可迭代对象转化为迭代器对象
2.while循环不断调用迭代器对象的__next__()方法,直到所有元素都被遍历。
3.在每次调用__next__()方法时,迭代器会返回下一个元素。如果没有更多元素,next()方法会抛出StopIteration异常。
4.for循环捕获StopIteration异常,结束循环。
字典的values()、keys()、items()都是可迭代对象:
print({"key1":123}.items().__iter__().__next__()) # ('key1', 123),先转换成了迭代器,然后取值。实际上这是执行了for循环原理。 # 所以得出for循环可直接遍历: for k,v in {"key1": 123}.items(): #print({"key1": 123}.items())------>dict_items([('key1', 123)])
print(k) # key1
print(v) # 123list、tuple工作原理:
""" for循环的原理就是这三个步骤,所以说list和tuple的工作原理也是这三步(如,list("hello")),先生成空列表;然后调用符串下面的__iter__方法,转成一个迭代器,然后调用迭代器下面的__next__方法拿到返回值,把返回值放在空列表里面。 """
四、迭代器与生成器区别
- 迭代器是一种工具,它可以遍历访问可迭代对象中的所有元素,但是它并不是可迭代对象。
- 生成器是一种特殊的迭代器,它可以通过函数来实现。生成器函数使用yield语句来返回一个值,每次调用生成器函数时,都会从上一次yield语句处继续执行。因此,生成器可以延迟计算,只有在需要时才会生成下一个元素,从而节省内存。
生成器与迭代器示例:
# 迭代器示例 class Counter: def __init__(self, n): self.n = n self.current = 0 def __iter__(self): return self def __next__(self): if self.current < self.n: num = self.current self.current += 1 return num else: raise StopIteration # 生成器示例 def counter(n): current = 0 while current < n: yield current current += 1
# 生成器表达式生成生成器gen = (x for x in range(1, 4))
print(type(gen)) # <class 'generator'>
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3计算序列中元素之和:
# 使用迭代器计算序列中所有元素的和 def iter_sum(nums): total = 0 it = iter(nums) while True: try: total += next(it) except StopIteration: break return total # 使用生成器计算序列中所有元素的和 def gen_sum(nums): total = 0 for num in nums: total += num return total综上所述,迭代器和生成器都是用来遍历访问序列中的元素的工具,它们之间的区别在于实现方式的不同。迭代器是一种工具,生成器是一种特殊的迭代器