这里介绍Python的迭代器、生成器、Iteration Protocol迭代协议
迭代器 迭代器是一个会记住遍历位置的对象。一方面,当迭代器中的元素被访问过后,就无法再次访问之前的元素。因为其是单向遍历的;另一方面,迭代器从第一个元素开始访问,当所有的元素都被访问完,就不能再次使用。具体地,可使用iter()函数通过 可迭代对象 创建 迭代器对象 。然后利用for循环、next函数等方法实现遍历访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from typing import Iteratorprint ("\n-------------访问迭代器方式1:for循环------------------------" )names = ["Bob" ,"Lucy" , "Tony" , "Amy" ] iterator1 = iter (names) print (f"iterator1 : {iterator1} " )print (f"iterator1 类型为迭代器: {isinstance (iterator1, Iterator)} " )for name in iterator1: print (f"name -->> {name} " ) print ("\n-------------访问迭代器方式2:next函数------------------------" )nums = (996 ,520 , 1314 , 69 ) iterator2 = iter (nums) print (f"iterator2 : {iterator2} " )print (f"iterator2 类型为迭代器: {isinstance (iterator2, Iterator)} " )while True : try : print (f"num -->> { next (iterator2) } " ) except StopIteration: print ("全部迭代结束" ) break print ("\n------------------访问迭代器方式3:直接将其转化为可迭代对象--------------------" )ages = [28 ,27 ,26 ,11 ] iterator3 = iter (ages) print (f"iterator3 : {iterator3} " )print (f"iterator3 类型为迭代器: {isinstance (iterator3, Iterator)} " )ages_tuple =tuple (ages) print (f"ages tuple : {ages_tuple} " )
生成器 生成器是一种特殊的迭代器。其有两种的创建方式:通过 生成器表达式或生成器函数 来创建。既然生成器也是迭代器的一种,自然也可以通过 for循环、next()函数、直接转换为可迭代对象 来访问
生成器表达式 所谓的生成器表达式,就是使用圆括号版本推导式,其返回的是一个生成器对象
1 2 3 4 5 6 7 8 9 10 11 12 from typing import Iterator, Generatortitle = "生成器表达式" print (f"------------ {title} -------------------" )g1 = (x**2 for x in range (5 )) print (f"type(g1) :" , type (g1))print (f"g1 类型为生成器: {isinstance (g1, Generator)} " )print (f"g1 类型为迭代器: {isinstance (g1, Iterator)} " )for num in g1: print (f"num->> {num} " )
生成器函数 Python中,包含 yield语句 的函数被称为生成器函数。其是一种特殊的函数。特殊点在于:
调用生成器凾数后,会直接返回一个新的生成器对象,但并没有执行生成器函数的函数体
对生成器对象每次通过next()函数遍历时,才会真正执行生成器函数中的函数体。具体执行流程:
yield语句用于产生返回值,同时函数立即返回;
当下一次遍历时,其会从上次返回的yield语句处继续往下执行
当生成器函数中的函数体全部执行完毕后,会抛出StopIteration异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from typing import Iterator, Generatortitle = "生成器函数" print (f"------------ {title} -------------------" )def my_generator (): print ("执行函数体:Hello One" ) yield 111 print ("执行函数体:Hello Two'" ) yield 2 print ("执行函数体:Hello Three'" ) yield 3392 g2 = my_generator() print (f"type(g2):" ,type (g2))print (f"g2 类型为生成器: {isinstance (g2, Generator)} " )print (f"g2 类型为迭代器: {isinstance (g2, Iterator)} " )while True : try : print (f"g2 res:{next (g2)} " ) except StopIteration: print ("生成器g2迭代结束" ) break
由于生成器函数的惰性计算特性,故非常适合用于大量数据的场景。这里展示如何通过生成器函数来计算斐波那契数列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from typing import Iterator, Generatortitle = "斐波那契数列的生成器函数" print (f"------------ {title} -------------------" )def fib_num_generator (max_count ): """ 斐波那契数列的生成器函数 max_count:遍历的最大次数限制100 """ count =0 a,b=0 ,1 while count < max_count: count = count +1 a, b = b, a+b yield a g3 = fib_num_generator(7 ) print (f"type(g3):" ,type (g3))print (f"g3 类型为生成器: {isinstance (g3, Generator)} " )print (f"g3 类型为迭代器: {isinstance (g3, Iterator)} " )res = tuple (g3) print (f"res : {res} " )
Iteration Protocol 迭代协议 传统实现 对于一个自定义对象而言,如果想将其视作为一个可迭代对象。就必须实现Iteration Protocol迭代协议。具体地:
自定义对象需要实现 __iter__() 方法,使其可以被视作为一个可迭代对象。具体地,该方法会返回一个迭代器对象
对于迭代器对象 而言,其一方面,需要实现 __next__() 方法,用于获取迭代器对象中的下一个元素;另一方面,通常还需要实现 __iter__() 方法,其会返回迭代器对象自身。此举是为了对 迭代器对象自身 进行迭代
通过下述例子不难看出,可迭代对象 和 迭代器对象 显然是两个对象 。通常我们进行实现时,也是提供两个对象。强烈不推荐将两个对象合二为一,即在 可迭代对象中去实现 __next__() 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 title = "迭代协议" print (f"------------ {title} -------------------" )class AddressBook : """ 通讯录 """ def __init__ (self ): self.list = [] def add_people_info (self,name,tel_no ): people_info = (name, tel_no) self.list .append( people_info ) def __iter__ (self ): """ 返回迭代器 """ return AddressBookIterator(self) class AddressBookIterator : """ 通讯录迭代器 """ def __init__ (self, address_book ): self.address_book_list = address_book.list self.current = 0 def __next__ (self ): if self.current >= len (self.address_book_list) : raise StopIteration people_info = self.address_book_list[ self.current ] self.current += 1 return people_info def __iter__ (self ): """ 返回迭代器 """ return self my_address_book = AddressBook() my_address_book.add_people_info("Aaron" , 110 ) my_address_book.add_people_info("Tom" , 120 ) my_address_book.add_people_info("Davlid" , 114 ) my_address_book.add_people_info("Lucy" , 119 ) print ("-------------------- 使用while循环 --------------------------" )my_address_book_iterator1 = iter ( my_address_book ) while True : try : people_info = next ( my_address_book_iterator1 ) print (f"people info : {people_info} " ) except StopIteration: break print ("-------------------- 使用for循环 --------------------------" )for people_info in my_address_book: print (f"people info ---->>>> {people_info} " )
由于我们在迭代器AddressBookIterator中实现了 __iter__() 方法,其会返回迭代器自身。故我们就可以对 迭代器对象 使用循环迭代了
1 2 3 4 5 6 7 print ("-------------------- 对迭代器进行迭代循环 --------------------------" )my_address_book_iterator2 = iter ( my_address_book ) for people_info in my_address_book_iterator2: print (f"PEOPLE INFO ---->>>> {people_info} " )
通过生成器来实现迭代协议 对于一个自定义对象而言,如果想将其视作为一个可迭代对象。我们在实现它的__iter__()方法时,还可以返回一个生成器。这样就避免了我们显式地提供一个迭代器对象了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 title = "通过生成器来实现迭代器" print (f"------------ {title} -------------------" )class AddressBook2 : """ 通讯录 """ def __init__ (self ): self.list = [] def add_people_info (self,name,tel_no ): people_info = (name, tel_no) self.list .append( people_info ) def __iter__ (self ): """ 返回生成器 """ for people_info in self.list : yield people_info my_address_book = AddressBook2() my_address_book.add_people_info("老王" , 52110 ) my_address_book.add_people_info("老李" , 52120 ) my_address_book.add_people_info("张三" , 52114 ) my_address_book.add_people_info("李四" , 52119 ) print ("\n-------------------- 使用while循环 --------------------------" )my_address_book_generator = iter ( my_address_book ) while True : try : people_info = next ( my_address_book_generator ) print (f"people info : {people_info} " ) except StopIteration: break print ("\n-------------------- 使用for循环 --------------------------" )for people_info in my_address_book: print (f"people info ---->>>> {people_info} " ) print ("\n-------------------- 对生成器进行迭代循环 --------------------------" )g1 = iter ( my_address_book ) g2 = iter ( g1 ) print (f"g1 : {g1} " )print (f"g2 : {g2} " )print (f"g1 is g2 : {g1 is g2} " )g3 = iter ( my_address_book ) for people_info in g3: print (f"PEOPLE INFO ---->>>> {people_info} " )
Note 当调用iter()方法时,Python完整的逻辑为:
Python 会先检查对象是否实现了__iter__()方法。如果实现了就调用__iter__()方法来获取迭代器对象
如果没有实现__iter__()方法,但是实现了__getitem__()方法。Python会自动创建一个迭代器,尝试按从索引0开始按顺序获取元素
如果尝试失败,Python 抛异常。提示该对象不可迭代
参考文献
Python编程·第3版:从入门到实践 Eric Matthes著
Python基础教程·第3版 Magnus Lie Hetland著
流畅的Python·第1版 Luciano Ramalho著