一、核心要义
1. 使用iter内置函数处理可迭代对象的方式
2. 如何使用Python实现经典的迭代器模式
3. 生成器函数工作原理
4. 使用生成器函数或生成器表达式代替经典的迭代器
5. 使用yield from语句合并生成器
二、代码示例
1、遍历单词序列回顾
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/26 20:42
# @Author : Maple
# @File : 01-遍历单词序列回顾.py
# @Software: PyCharm
from collections import abc
import re
import reprlibRE_WORDS = re.compile('\w+')
class Sentence:def __init__(self,text):self.text = textself.words = RE_WORDS.findall(text)def __getitem__(self, pos):return self.words[pos]def __len__(self):return len(self.words)def __repr__(self):return 'Sentences(%s) ' % reprlib.repr(self.text)class Foo:def __iter__(self):passif __name__ == '__main__':# 1. 迭代Sentence对象s = Sentence('I love this word')"""s可迭代的原因1. 当迭代一个对象的时候,解释器会检查对象是否实现了__iter__方法,如果实现了,就调用它,获取一个迭代器2. 如果没有实现__iter__方法,但是实现了__getitem__方法,Python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素3. 如果尝试失败,Python抛出TypeError异常,通常会提示"C object is not itereble""""for word in s:"""Ilovethisword"""print(word)# 2. Sentence对象是否可通过issubclass和isinstance测试# 虽然Sentence对象实现了 __getitem__,因为是可迭代的,但其实无法通过测试# 所以如果要判断一个对象是否可迭代,最好不要使用该方式,而是直接iter(对象),如果没有报错,就说明对象是可迭代的print(issubclass(Sentence,abc.Iterable)) # Falseprint(isinstance(s,abc.Iterable)) # False## 获取迭代器s = iter(s)## 迭代迭代器中的元素for word in s:print(word)# 3.一个类只要实现了__iter__方法,那么其对象就是可迭代的,并且可以通过issubclass和isinstance测试f = Foo()print(issubclass(Foo, abc.Iterable)) # Trueprint(isinstance(f, abc.Iterable)) # True
2、可迭代对象和迭代器
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/26 21:03
# @Author : Maple
# @File : 02-可迭代对象和迭代器.py
# @Software: PyCharm"""
两者的定义和关系:1.可迭代对象: 实现了__iter__方法2.迭代器: 实现了__iter__和__next__方法"""
import re
import reprlibRE_WORDS = re.compile('\w+')"""
1.利用__iter__方法定义一个类,当调用iter方法时,返回一个迭代器
2.对迭代器进行遍历的时候,不断调用next方法,返回迭代器中的每一个元素
"""
class Sentence:def __init__(self,text):self.text = textself.words = RE_WORDS.findall(text)def __iter__(self):return SentenceIterator(self.words)def __len__(self):return len(self.words)def __repr__(self):return 'Sentences(%s) ' % reprlib.repr(self.text)class SentenceIterator:"""定义迭代器:__iter__方法和__next__方法"""def __init__(self,words):self.words = wordsself.index = 0def __iter__(self):"""迭代器应该一直可以迭代,所以返回自身,在哪里会用到这个特性? """return selfdef __next__(self):try:word = self.words[self.index]except IndexError:raise StopIteration()self.index += 1return wordif __name__ == '__main__':# 1. Sentence测试s = Sentence('I love this world')for word in s:print(s)# 2.s2是一个迭代器## 2-1 直接调用迭代器类 生成一个迭代器print('******2-1 直接调用迭代器类 生成一个迭代器************')s2 = SentenceIterator(['we', 'are', 'the', 'world'])print('s2:',s2)index = 0for word in s2:"""weare"""index += 1if index > 1:print(word)breakelse:print(word)## 2-2 调用迭代器的iter方法,返回迭代器本身print('******2-2 调用迭代器的iter方法,返回迭代器本身************')s3 = iter(s2)print('s3:',s3)for word in s3:"""在2-1的基础上,继续迭代,直到迭代结束theworld"""print(word)
3、生成器函数
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/26 21:42
# @Author : Maple
# @File : 03-生成器函数.py
# @Software: PyCharm"""
对上一小节的Sentence进一步改造:__iter__方法不用返回一个迭代器.而是直接将其定义为一个生成器函数
--什么是生成器函数: 包含yield关键
"""
import re
import reprlibRE_WORDS = re.compile('\w+')# 利用生成器函数,实现Sentence类对象的可迭代(不用在__iter__方法中返回迭代器)
class Sentence1:def __init__(self,text):self.text = textself.words = RE_WORDS.findall(text)def __repr__(self):return 'Sentences(%s) ' % reprlib.repr(self.text)def __iter__(self):for word in self.words:yield wordreturndef __len__(self):return len(self.words)# 对Sentence1进一步改造:self.words不需要提前生成(是一个列表)
class Sentence2:def __init__(self,text):self.text = textdef __repr__(self):return 'Sentences(%s) ' % reprlib.repr(self.text)def __iter__(self):# RE_WORDS.finditer(self.text)返回的是一个迭代器# 通过for循环遍历迭代器for match in RE_WORDS.finditer(self.text):yield match.group()return# 利用生成器表达式,进一步改造Sentence
class Sentence3:def __init__(self,text):self.text = textdef __repr__(self):return 'Sentences(%s) ' % reprlib.repr(self.text)def __iter__(self):# 直接返回生成器表达式,与yield的作用相同return (match.group() for match in RE_WORDS.finditer(self.text))if __name__ == '__main__':# 1.Sentence1测试s = Sentence1('I love this world')## 调用iter返回一个生成器s_iter = iter(s)## 遍历生成器(本质就是一个迭代器)for word in s_iter:"""Ilovethisword"""print(word)# 2.Sentence2测试print('******2.Sentence2测试***********')s2 = Sentence2('Please stay with me forerver')## 调用iter返回一个生成器s_iter2 = iter(s2)## 遍历生成器(本质就是一个迭代器)for word in s_iter2:"""Pleasestaywithmeforerver"""print(word)# 3.Sentence3测试print('******3.Sentence3测试***********')s3 = Sentence3("Let's start study Python")## 调用iter返回一个生成器s_iter3 = iter(s3)## 遍历生成器(本质就是一个迭代器)for word in s_iter3:"""LetsstartstudyPython"""print(word)
4、等差数列生成器
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/27 20:03
# @Author : Maple
# @File : 04-等差数列生成器.py
# @Software: PyCharm# 自定义等差序列生成器
from decimal import Decimalclass ArithmeticProgression:def __init__(self,start,step,end=None):self.start = startself.step = stepself.end = enddef __iter__(self):index = 0result = type(self.start + self.step)(self.start)forever = self.end is Nonewhile forever or result < self.end:yield resultindex += 1result = self.start + self.step * indexclass ArithmeticProgression2:def __init__(self,start,step,end=None):self.start = startself.step = stepself.end = enddef __iter__(self):ap_gen = itertools.count(self.start, self.step)if self.end is not None:ap_gen = itertools.takewhile(lambda n: n < self.end, itertools.count(self.start, self.step))# 直接返回迭代器return ap_genif __name__ == '__main__':# 1. 自定义等差序列生成器测试## 1-1: 起始值1,步长为0.5,末项是5(不包含)a1 = ArithmeticProgression(1,0.5,5)for i in a1:"""1.01.52.02.53.03.54.04.5"""print(i)## 1-2: 起始值1,步长为Decimal(0.2),末项是2(不包含)a2 = ArithmeticProgression(1, Decimal(.2),2)for i in a2:"""11.2000000000000000111022302461.4000000000000000222044604921.6000000000000000333066907391.800000000000000044408920985"""print(i)#2.利用系统自带的itertools模块生成等差数列print('*****2.利用系统自带的itertools模块生成等差数列********')import itertools## 2-1 注意:由于该模块方法并没有end参数,如果通过for循环遍历,会生成无穷的序列..直到内存爆掉g = itertools.count(1,0.5)print(next(g)) # 1print(next(g)) # 1.5## 2-2 为了修复上面提到的弊端,可以利用# 该方法返回的是一个迭代器,然后接收两个参数:当第二个迭代器中的元素,不满足第一个参数设置的条件时,会终止第二个迭代器迭代gen = itertools.takewhile(lambda n: n <3,itertools.count(1,0.5))print(list(gen)) # [1, 1.5, 2.0, 2.5]# 3.利用itertools.takewhile改造ArithmeticProgressionprint('******3.利用itertools.takewhile改造ArithmeticProgression**********************')a3 = ArithmeticProgression2(1, 0.5, 5)print(list(a3)) # [1, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]a4 = ArithmeticProgression2(1, Decimal(.2), 2)print(list(a4)) # [1, Decimal('1.200000000000000011102230246'), Decimal('1.400000000000000022204460492'), Decimal('1.600000000000000033306690738'), Decimal('1.800000000000000044408920984')]
5、yield from
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/27 20:36
# @Author : Maple
# @File : 05-yield from.py
# @Software: PyCharm"""
当生成器函数需要产出另一个生成器 生成的值,传统的解决方案是for循环嵌套"""def chain(*iterables):for it in iterables:for i in it:yield i# 利用yield from
def chain2(*iterables):for it in iterables:yield from itif __name__ == '__main__':#1.chain方法测试s = 'ABC't = tuple(range(3))# ['A', 'B', 'C', 0, 1, 2]print(list(chain(s,t)))#2.chain2方法测试s = 'EDF't = tuple(range(3))# ['A', 'B', 'C', 0, 1, 2]print(list(chain2(s, t))) # ['E', 'D', 'F', 0, 1, 2]
6、可迭代的归约函数
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/27 20:45
# @Author : Maple
# @File : 06-可迭代的归约函数.py
# @Software: PyCharmif __name__ == '__main__':print(all([1,2,3])) #Trueprint(all([1,0,3])) #Falseprint(any([1,2,3])) #Trueprint(any([1,0,3])) #Trueprint(any([0,0.0])) #Falseprint(any([])) #Falseg = (n for n in [0,0.0,7,8])print(any(g)) #True
7、迭代器哨值
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/2/27 20:50
# @Author : Maple
# @File : 07-迭代器哨值.py
# @Software: PyCharm"""
迭代器函数iter可以传入两个参数:其中第一个必须是一个可调用对象,第二个就是哨值,当可调用对象返回这个值时,迭代器将停止迭代
"""
from random import randintdef f6():# 返回[1-6]区间的整数return randint(1,6)if __name__ == '__main__':# print(randint(1,6))f6_iter = iter(f6,1)for i in f6_iter:# 如下结果表明,第三次迭代的值是1,然后迭代终止"""24"""print(i)