文章目录
- 迭代器
- 1. 可迭代对象
- 2. 迭代器
- 3. 动手写一个可迭代的类
- 4. 封装到一个类中
- 5. 迭代器的应用
- range和xrange
- 使用迭代器生成斐波那契数列
- 列表解析式
- 生成器
- 生成器的函数实现
- 通过send方式启动生成器
迭代器
1. 可迭代对象
from collections import Interable
print(isinstance([11,22,33],Interable))
如果要使一个对象是可迭代的,必须重写__iter__方法,这样才能通过Interable的检验,但是此时仍不能通过for函数遍历,所以仅仅是可迭代对象,而不是迭代器。
示例代码:
from collections.abc import Iterable
class ClassMate():
def __init__(self) -> None:
self.name = list()
def add(self,name):
self.name.append(name)
def __iter__(self):
pass
classmate = ClassMate()
print("判断ClassMate是否是可迭代对象:",isinstance(classmate,Iterable))
2. 迭代器
迭代器必须重写__iter__方法和__next__方法,其中,__iter__方法必须返回一个对象的引用。
可以使用for循环遍历迭代器,for temp in xxx_obj的执行过程:
- 判断对象
xxx_obj是否有__iter__方法(可迭代对象) - 在第一步成立的条件下,调用
iter函数,得到对象xxx_obj的__next__的返回值,其返回值就是一个迭代器 - 自动调用迭代器的
next函数,即调用迭代器的__next__方法,直到抛出StopIteration异常。
对于这样一个for循环
for i in seq:
do_something_to(i)
实际上是这样工作的
fetch_iterator = iter(q)
while True:
try:
i = fetch_iterator.next()
except StopIteration:
break
do_something_to(i)
迭代器非常重要,python3中字典、文件、列表都是可迭代的对象。
3. 动手写一个可迭代的类
from collections.abc import Iterable
from collections.abc import Iterator
# 实现将一个可迭代的类
class ClassMate(object):
def __init__(self) -> None:
self.names = list()
def add(self,name):
self.names.append(name)
def __iter__(self):
# 返回一个迭代器的实例对象
return ClassIterator(self)
class ClassIterator(object):
def __init__(self,obj):
self.obj = obj
# 通过实例属性记录前一次指针的位置
self.current_num = 0
def __iter__(self):
pass
def __next__(self):
"""next方法执行此方法"""
if self.current_num < len(self.obj.names):
# 手动防止越界
ret = self.obj.names[self.current_num]
self.current_num += 1
return ret
else:
raise StopIteration
# for 循环会自动捕获错误
classmate = ClassMate()
classmate.add('小王')
classmate.add('小美')
classmate.add('小明')
class_iterator = iter(classmate)
print("判断classmate是否是可迭代对象",isinstance(classmate,Iterable))
print("判断class_iterator是否是迭代器",isinstance(class_iterator,Iterator))
for name in classmate:
print(name)
# Output:
# 判断classmate是否是可迭代对象 True
# 判断class_iterator是否是迭代器 True
# 小王
# 小美
# 小明
执行过程:

注意:
- 可迭代对象A的__iter__返回一个迭代器的 实例对象B, 迭代器B初始化时需要将 可迭代对象A 传入 初始化方法;
- 迭代器实例对象通过一个 实例属性self.current_num记录当前读取的位置
- 需要手动防止越界,否则for语句会不断执行__next__,并捕获返回的None,此时,可通过抛出 StopIteration 错误,for循环会自动捕获。
4. 封装到一个类中
上面的方法使用了两个类,有点浪费资源,我们将其全部封装到一个类中
from collections.abc import Iterable
from collections.abc import Iterator
# 实现将一个可迭代的类
class ClassMate(object):
def __init__(self):
self.names = list()
self.current_count = 0
def add(self,name):
self.names.append(name)
def __iter__(self):
# 返回自身实例对象
return self
def __next__(self):
if self.current_count<len(self.names):
ret = self.names[self.current_count]
self.current_count+=1
return ret
else:
raise StopIteration
classmate = ClassMate()
classmate.add('小王')
classmate.add('小美')
classmate.add('小明')
class_iterator = iter(classmate)
print("判断classmate是否是可迭代对象",isinstance(classmate,Iterable))
print("判断class_iterator是否是迭代器",isinstance(class_iterator,Iterator))
for name in classmate:
print(name)
# Output:
# 判断classmate是否是可迭代对象 True
# 判断class_iterator是否是迭代器 True
# 小王
# 小美
# 小明
5. 迭代器的应用
range和xrange
【补充】range和xrange的区别:
range和xrange的区别:在python2中range返回数值,xrange返回一个可迭代的对象,占用了极小的空间,
在python3中,range也返回的是一个可迭代对象了。
使用迭代器生成斐波那契数列
例如可以使用迭代器实现斐波那契数列
# 一般实现
a = 0
b = 1
i = 0
while i<10:
a,b = b,a+b
print(a)
# 迭代器实现
class Fibonacci(object):
def __init__(self,all_num):
self.max_num = all_num
self.count_num = 0
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
if self.count_num < self.max_num:
self.count_num += 1
self.a,self.b = self.b,self.a+self.b
return self.a
else:
raise StopIteration
fibo = Fibonacci(10)
for num in fibo:
print(num)
列表解析式
列表解析,List comprehensinos,来自于Haskell语言,可以取代lambda、map和filter函数的部分使用场景。
语法[expr for iter_val in iterable if cond_expr]
例1:计算一个序列成员的平方
map(lambda x:x**2,range(6))
# 或者
[x**2 for x in range(6)]
例2:挑选出序列seq中的奇数
filter(lambda x: x%2,seq)
# or
[x for x in seq if x%2]
例3:计算文本中的单词个数【列表解析式支持多重循环和多个if语句,执行顺序从左到右】
f = open('test.txt','r')
len([word for line in f for word in line.split()])
生成器表达式和列表解析式很类似:(expr for iter_val in iterable if cond_expr),因为生成器表达式使用了“延迟计算”(lazy evaluation),所以在内存上比列表解析式更加有效。
