一、核心要义
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 reprlib
RE_WORDS = re.compile('\w+')
class Sentence:
def __init__(self,text):
self.text = text
self.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):
pass
if __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:
"""
I
love
this
word
"""
print(word)
# 2. Sentence对象是否可通过issubclass和isinstance测试
# 虽然Sentence对象实现了 __getitem__,因为是可迭代的,但其实无法通过测试
# 所以如果要判断一个对象是否可迭代,最好不要使用该方式,而是直接iter(对象),如果没有报错,就说明对象是可迭代的
print(issubclass(Sentence,abc.Iterable)) # False
print(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)) # True
print(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 reprlib
RE_WORDS = re.compile('\w+')
"""
1.利用__iter__方法定义一个类,当调用iter方法时,返回一个迭代器
2.对迭代器进行遍历的时候,不断调用next方法,返回迭代器中的每一个元素
"""
class Sentence:
def __init__(self,text):
self.text = text
self.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 = words
self.index = 0
def __iter__(self):
"""迭代器应该一直可以迭代,所以返回自身,在哪里会用到这个特性? """
return self
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word
if __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 = 0
for word in s2:
"""we
are
"""
index += 1
if index > 1:
print(word)
break
else:print(word)
## 2-2 调用迭代器的iter方法,返回迭代器本身
print('******2-2 调用迭代器的iter方法,返回迭代器本身************')
s3 = iter(s2)
print('s3:',s3)
for word in s3:
"""
在2-1的基础上,继续迭代,直到迭代结束
the
world
"""
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 reprlib
RE_WORDS = re.compile('\w+')
# 利用生成器函数,实现Sentence类对象的可迭代(不用在__iter__方法中返回迭代器)
class Sentence1:
def __init__(self,text):
self.text = text
self.words = RE_WORDS.findall(text)
def __repr__(self):
return 'Sentences(%s) ' % reprlib.repr(self.text)
def __iter__(self):
for word in self.words:
yield word
return
def __len__(self):
return len(self.words)
# 对Sentence1进一步改造:self.words不需要提前生成(是一个列表)
class Sentence2:
def __init__(self,text):
self.text = text
def __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 = text
def __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:
"""
I
love
this
word
"""
print(word)
# 2.Sentence2测试
print('******2.Sentence2测试***********')
s2 = Sentence2('Please stay with me forerver')
## 调用iter返回一个生成器
s_iter2 = iter(s2)
## 遍历生成器(本质就是一个迭代器)
for word in s_iter2:
"""
Please
stay
with
me
forerver
"""
print(word)
# 3.Sentence3测试
print('******3.Sentence3测试***********')
s3 = Sentence3("Let's start study Python")
## 调用iter返回一个生成器
s_iter3 = iter(s3)
## 遍历生成器(本质就是一个迭代器)
for word in s_iter3:
"""
Let
s
start
study
Python
"""
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 Decimal
class ArithmeticProgression:
def __init__(self,start,step,end=None):
self.start = start
self.step = step
self.end = end
def __iter__(self):
index = 0
result = type(self.start + self.step)(self.start)
forever = self.end is None
while forever or result < self.end:
yield result
index += 1
result = self.start + self.step * index
class ArithmeticProgression2:
def __init__(self,start,step,end=None):
self.start = start
self.step = step
self.end = end
def __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_gen
if __name__ == '__main__':
# 1. 自定义等差序列生成器测试
## 1-1: 起始值1,步长为0.5,末项是5(不包含)
a1 = ArithmeticProgression(1,0.5,5)
for i in a1:
"""1.0
1.5
2.0
2.5
3.0
3.5
4.0
4.5
"""
print(i)
## 1-2: 起始值1,步长为Decimal(0.2),末项是2(不包含)
a2 = ArithmeticProgression(1, Decimal(.2),2)
for i in a2:
"""
1
1.200000000000000011102230246
1.400000000000000022204460492
1.600000000000000033306690739
1.800000000000000044408920985
"""
print(i)
#2.利用系统自带的itertools模块生成等差数列
print('*****2.利用系统自带的itertools模块生成等差数列********')
import itertools
## 2-1 注意:由于该模块方法并没有end参数,如果通过for循环遍历,会生成无穷的序列..直到内存爆掉
g = itertools.count(1,0.5)
print(next(g)) # 1
print(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改造ArithmeticProgression
print('******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 it
if __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: PyCharm
if __name__ == '__main__':
print(all([1,2,3])) #True
print(all([1,0,3])) #False
print(any([1,2,3])) #True
print(any([1,0,3])) #True
print(any([0,0.0])) #False
print(any([])) #False
g = (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 randint
def 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,然后迭代终止
"""2
4
"""
print(i)