迭代器和生成器
迭代器实现
# for 循环遍历列表、元组和字典等,这些对象都是可迭代的,因此它们都属于迭代器 '''__iter__(self):该方法返回一个迭代器(iterator),迭代器必须包含一个__next__()方法, 该方法返回迭代器的下一个元素。''' """__reversed__(self):该方法主要为内建的 reversed() 反转函数提供支持, 当程序调用 reversed() 函数对指定迭代器执行反转时,实际上是由该方法实现的。""" # 定义一个代表斐波那契数列的迭代器 class Fibs: def __init__(self, len): self.first = 0 self.sec = 1 self.__len = len # 定义迭代器所需的__next__方法 def __next__(self): # 如果__len__属性为0,结束迭代 if self.__len == 0: raise StopIteration # 完成数列计算: self.first, self.sec = self.sec, self.first + self.sec # 数列长度减1 self.__len -= 1 return self.first # 定义__iter__方法,该方法返回迭代器 def __iter__(self): return self # 创建Fibs对象 fibs = Fibs(10) # 获取迭代器的下一个元素 print(next(fibs)) # 使用for循环遍历迭代器 for el in fibs: print(el, end=' ') # 将列表转换为迭代器 my_iter = iter([3,4,5,'java']) # 依次获取迭代器的下一个元素 print(my_iter.__next__()) # 3 print(my_iter.__next__()) # 4
生成器
# 创建生成器需要两步操作: # 1.定义一个包含 yield 语句的函数。 # 2.调用第 1 步创建的函数得到生成器 def test(val, step): print("--------函数开始执行------") cur = 0 # 遍历0~val for i in range(val): # cur添加i*step cur += i * step # print(cur,end=' ') yield cur # yield cur 语句的作用有两点: # 每次返回一个值,有点类似于 return 语句。 # 冻结执行,程序每次执行到 yield 语句时就会被暂停。 """在程序被 yield 语句冻结之后,当程序再次调用 next() 函数获取生成器的下一个值时, 程序才会继续向下执行。""" """需要指出的是,调用包含 yield 语句的函数并不会立即执行,它只是返回一个生成器。 只有当程序通过 next() 函数调用生成器或遍历生成器时,函数才会真正执行。""" # 执行函数,返回生成器 t = test(10, 2) print('=================') # 获取生成器的第一个值 print(next(t)) # 0,生成器“冻结”在yield处 print(next(t)) # 2,生成器再次“冻结”在yield处 """从上面的输出结果不难看出,当程序执行 t = test(10, 2) 调用函数时, 程序并未开始执行 test() 函数;当程序第一次调用 next(t) 时,test() 函数才开始执行。 Python 2.x 不使用 next() 函数来获取生成器的下一个值,而是直接调用生成器的 next() 方法。 也就是说,在 Python 2.x 中应该写成 t.next()。 当程序调用 next(t) 时,生成器会返回 yield cur 语句返回的值(第一次返回 0), 程序被“冻结”在 yield 语句处,因此可以看到上面生成器第一次输出的值为 0。 当程序第二次调用 next(t) 时,程序的“冻结”被解除,继续向下执行,这一次循环计数器 i 变成 1, 在执行 cur += i * step 之后,cur 变成 2 , 生成器再次返回 yield cur 语句返回的值(这一次返回 2), 程序再次被“冻结”在该 yield 语句处,因此可以看到上面生成器第二次输出的值为 2。 """ # 由于前面两次调用 next() 函数已经获取了生成器的前两个值,因此此处循环时第一次输出的值就是 6 for x in t: print(x,end=' ')#6 12 20 30 42 56 72 90 print("***************************") #再次创建生成器 t = test(10, 1) #将生成器转换成列表 print (list (t)) #再次创建生成器 t = test(10, 3) #将生成器转换成元组 print(tuple(t)) """ 使用生成器的优势: 1.当使用生成器来生成多个数据时,程序是按需获取数据的,它不会一开始就把所有数据都生成出来, 而是每次调用 next() 获取下一个数据时,生成器才会执行一次,因此可以减少代码的执行次数。 比如前面介绍的示例,程序不会一开始就把生成器函数中的循环都执行完成, 而是每次调用 next() 时才执行一次循环体。 2.当函数需要返回多个数据时,如果不使用生成器,程序就需要使用列表或元组来收集函数返回的多个值, 当函数要返回的数据量较大时,这些列表、元组会带来一定的内存开销。 如果使用生成器就不存在这个问题,生成器可以按需、逐个返回数据。 3.使用生成器的代码更加简洁。 """