文章目录
迭代器
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),所以在内存上比列表解析式更加有效。
生成器
生成器是一种特殊的迭代器,你只能对它迭代一次,因为它在运行时生成值,并不存储进内存。
生成器的函数实现
生成器由两种定义方式,第一种即是将列表解析式的[]
换成()
,不过更常用的是函数的形式:
例子:使用生成器生成斐波那契数列
def create_num(all_num):
a,b = 0,1
count_num = 0
while count_num < all_num:
yield a
a,b = b,a+b
count_num += 1
obj = create_num(10)
for i in obj:
print(i)
# 当执行到yield的时候,生成器会暂停输出,并返回结果
可见,生成器和函数的一个重要区别就是函数名()
h会立即执行,而obj = 生成器()
只是创建一个生成器对象,通过next obj
的方式,返回内部yield
对应的值,下次调用next
的时候,则会从上次yield
暂停的地方开始执行。
当然也可以使用while True
的方式遍历生成器对象,但是 需要手动捕获异常 作为结束条件。
def create_num(all_num):
a,b = 0,1
count_num = 0
while count_num < all_num:
yield a
a,b = b,a+b
count_num += 1
obj = create_num(10)
while True:
try:
ret = next(obj)
print(ret)
except Exception as ret:
print(ret.value)
break
也可以创建多个生成器,互不影响,类似于类创建的对象之间的关系。
通过send方法从外向内给生成器传值
生成器的yield不仅能够将生成器内部的值传出来,还能够接收外部传送的数据,需要send方法传递。
demo:
def create_num(all_num):
a,b = 0,1
count_num = 0
while count_num < all_num:
ret_in = yield a
print(">>>ret>>>",ret_in)
a,b = b,a+b
count_num += 1
obj = create_num(10)
ret_out = next(obj)
print(ret_out)
ret_out = obj.send("hahahah")
print(ret_out)
首先,执行yield a,将a的值传递给ret_out,然后在send调用时,继续执行这一行,但是yield已经没有返回值,这里传递给ret_in的值,就是send中的参数。
注意:send不能在一开始处就调用,否则会报错。
小结:
拥有一个__iter__
和__next__
方法的类的实例对象叫做迭代器,可以节省内存空间;拥有yield
的函数叫生成器,可以在yield
处暂停执行,并在下次调用时从上次暂停的地方重新开始,两者都可以通过next
进行遍历。