迭代器
实现__next__方法
如果一个类实现了__next__方法,那么这个类的对象是一个迭代器
例如:
class myRange:
def __init__(self,start=0,end=5):
self.start=start
self.end=end
def __next__(self):
self.start+=1
if self.start>self.end:
raise StopIteration
return self.start
此时类myRange的一个实例对象就是一个迭代器,所谓迭代,就是利用next,一个一个的读取迭代器里面的值,直到终止条件抛出StopIteration异常为止。
实现__iter__方法
需要注意的是,上面定义的类myRange目前不是可迭代对象,不可以直接在循环中迭代的,例如:
我们需要实现__iter__方法才使得myRange类是可以在循环中迭代的
利用迭代器的设计方法构造自己的迭代器
我们已经知道,只要设计好__next__和__iter__,就可以构造迭代器了.
我们以range函数举例,
range函数返回的是一个可迭代序列,而且可以利用list函数直接得到序列
我们现在就构造自己的range函数
class myRange:
def __init__(self,*params):
self.stride=1
assert type(params)==tuple
if len(params)==0:
raise TypeError("range expected 1 arguments, got 0")
if len(params)>3:
raise TypeError("range expected at most 3 arguments, got %d"%len(params))
assert len(params)>=1 and len(params)<=3
#上面的代码判断边界条件
if len(params)==1:
#仅仅传进来一个值,那么这个值就做为终止位置
end_value=params[0]
self.start=0
if end_value<=0:
self.end=0
else:
self.end=end_value
elif len(params)==2:
#传进来两个值
start_value,end_value=params
if start_value>=end_value:
self.start=0
self.end=0
else:
self.start=start_value
self.end=end_value
else:
assert len(params)==3
start_value,end_value,stride=params
if stride==0:
raise ValueError('range() arg 3 must not be zero')
elif (start_value<end_value and stride<0) or start_value==end_value:
self.start=0
self.end=0
else:
#start_value<end_value stride>0
#start_value>end_value stride<0
self.start=start_value
self.end=end_value
self.stride=stride
self.start-=self.stride
self.end-=self.stride
def __next__(self):
assert self.stride!=0
if self.stride>0:
if self.start>=self.end:
raise StopIteration
self.start+=self.stride
return self.start
else:
if self.start<=self.end:
raise StopIteration
self.start+=self.stride
return self.start
def __iter__(self):
return self
打印验证结果:
生成器
生成器可以简单的理解为包含有yield语句的函数,生成器返回的其实就是一个可迭代的迭代器。这个迭代器里面的数据每当返回一个的时候,迭代器就会被“冻住”,固定在那个位置,下次再次迭代的时候,迭代器会从上一次定住的那个点的位置开始执行
举个例子
我们可以看到,这10000000个数并不是在内存中的,只有当迭代generator(generator其实是一个可迭代的迭代器)的时候,才会返回出来一个结果,而且这个结果返回后就被丢弃了
我们再举一个例子,创建可以展平列表元素的生成器:
这里面需要注意的是,对于数字来说上面的代码是没有问题的,因为数字是不可迭代的对象,所以是有终止条件的,然而假如列表中含有字符串,我们要知道
- 第一,字符串是可迭代的对象,我们不希望一个字符一个字符的输出,而是希望整体输出字符串,那么就不能迭代字符串
- 第二,python中即使是单个字符也是字符串,所以会陷入到无穷递归中
解决办法是添加isinstance判断一下数据类型,如果是字符串,那么就直接yield