一、可迭代对象:有_ _iter_ _方法
二、迭代器:同时有 _ _iter_ _和 _ _next_ _方法
三、生成器 :yield / (i for in in range(n))
四、字典、元祖、列表等可迭代对象
可以被迭代若干次
可以被转换成迭代器对象(使用iter方法)
一、可迭代对象
首先来看一下,python 2.7版本range的实现原理:
from typing import Iterable def range_test(stop): start = 0 result = [] while start < stop: result.append(start) start += 1 return result if __name__ == '__main__': print(isinstance(range_test(10),Iterable)) # 判断返回结果是不是可迭代,True print(range_test(10)) # output:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
这种方式,需要多少数据,全部存在list中,在数据量比较大的情况下,是非常耗内存的。
那么如何实现高性能(省内存)的呢?请看实现:
省内存:延迟生成数据,节省内存
from typing import Iterable # Next含有__next__()方法,每次变量+1,一次只占用一个数的内存 class Next(object): """Next class""" def __init__(self, stop, start=-1): self.start = start self.stop = stop def __next__(self): if self.start >= self.stop - 1: raise StopIteration self.start += 1 return self.start # 含有__iter__()方法,return调用Next class MyRange: def __init__(self, stop): self.stop = stop def __iter__(self): return Next(self.stop) if __name__ == '__main__': print(Next(10).__next__()) # 0
print(isinstance(MyRange(10), Iterable)) # True (因为MyRange有__iter__()) print(isinstance(Next(10), Iterable)) # False (Next没有__iter__()) for i in MyRange(10): # for循环自动执行__iter__,__next__方法 print(i) # 0,1,2,3,4,5,6,7,8,9 # 下面用while循环,解释下运行的原理。(for的原理,for是一种语法,会自动执行__next__()) num = MyRange(10).__iter__() i = 0 while i < 10: print(num.__next__()) # 0,1,2,3,4,5,6,7,8,9 i += 1
总结:
1.可迭代对象:对象必须要有_ _iter_ _()方法
(能被for迭代的对象 都是可迭代对象):这里给你们捋一捋思路,能够被for迭代是因为for会自动调用__iter__(),那有了__iter__,就符合可迭代对象的基本定义了。
常见的可迭代对象:list,str,tuple
我们也可以用dir()自省,来判断某个每个对象是不是可迭代对象。自省的结果中有_ _iter_ _(),说明是可迭代对象。
上面的例子可以用hasattr()验证,返回值是bool类型,可迭代对象是否有_ _iter_ _():--------hasattr() 函数用于判断对象是否包含对应的属性。
print(hasattr(list,"__iter__")) # True print(hasattr(str, "__iter__")) # True print(hasattr(tuple, "__iter__")) # True
2.for 用在迭代的场景,能用for,就不用while。不会越界,比较安全。(for 循环不会越界,while循环很容易越界,while通常用在自由执行循环的场景
二、迭代器
内置函数next():
首先来看下next()源码:
def next(iterator, default=None): # real signature unknown; restored from __doc__ """ next(iterator[, default]) Return the next item from the iterator. If default is given and the iterator is exhausted, it is returned instead of raising StopIteration. """ pass
看见没?想要使用next()函数,参数必须是个迭代器!!!
来看看list,能不能使用next()方法:
print(next([1,2])) """ print(next([1,2])) TypeError: 'list' object is not an iterator """
哎呀,报错了:list对象不是一个迭代器!!
因为next([1,2])实际上执行的是[1,2]._ _next_ _(). 但是list没有_ _next_ _()属性,就会报错(list不是一个迭代器)
print(hasattr(list,"__next__")) # False
那什么是迭代器??
迭代器:
必须同时有 _ _iter_ _和 _ _next_ _方法,才是迭代器
核心:通过_ _next_ _方法记住迭代的位置,有没有迭代完毕。
举例:上面例子中的class Next,里面有_ _next_ _,是不是迭代器呢?
class Next(object): """Next class""" def __init__(self, stop, start=-1): self.start = start self.stop = stop def __next__(self): if self.start >= self.stop - 1: raise StopIteration self.start += 1 return self.start if __name__ == '__main__': print(isinstance(Next(2),Iterator)) #False 因为虽然含有__next__属性,但是没有__iter__属性,不是一个迭代器
栗子2:
class Fruit: """ class Fruit :param :raise Raise StopIteration if index out of len(friuts) """ def __init__(self): self.fruit = ["apple","peach","banana","grape"] self.index = 0 def __next__(self): if self.index >= len(self.fruit): raise StopIteration self.index += 1 return self.fruit[self.index - 1] if __name__ == '__main__': fruit = Fruit() for item in fruit: #对fruit对象进行迭代,不是一个可迭代对象,因为没有_ _iter_ _ print(item) """ for item in fruit: TypeError: 'Fruit' object is not iterable """
下面加上一个_ _iter_ _方法,再来看一下
class Fruit(object): """ class Fruit :param :raise Raise StopIteration if index out of len(friuts) """ def __init__(self): self.fruit = ["apple", "peach", "banana", "grape"] self.index = 0 def __iter__(self): return iter(self.fruit) def __next__(self): if self.index >= len(self.fruit): raise StopIteration self.index += 1 return self.fruit[self.index - 1] if __name__ == '__main__': ft = Fruit() for item in ft: print(item) """ apple peach banana grape """
print(isinstance(ft,Iterator)) # True 是迭代器,同时含有__iter__ 和__next__
iter()方法-----iter() 函数用来把可迭代对象生成迭代器
上面的_ _iter_ _()方法的返回值要是一个“迭代器”---严格的说,返回值必须有_ _next_ _()方法,比如开始的例子,MyRange中的_ _iter__()的返回值,调用了Next,有_ _next_ _()方法。
总结:
①迭代器是一个特殊的可迭代对象,特殊在它还有_ _next_ _()方法
注意:上面的 _ _next_ _() 没有被for循环调用,for循环只是自动调用了_ _iter__()。因为自己写的__iter__(),不会去调用_ _next_ _()
那么怎么才能调用_ _next_ _()方法呢?
继承迭代器(直接继承迭代器(已经实现了_ _iter_ _),不需要自己写_ _iter__()方法了)
from typing import Iterator class Fruit(Iterator): """ class Fruit :param :raise Raise StopIteration if index out of len(friuts) """ def __init__(self): self.fruit = ["apple", "peach", "banana", "grape"] self.index = 0 def __next__(self): print("这个是__next__ 方法") if self.index >= len(self.fruit): raise StopIteration self.index += 1 return self.fruit[self.index - 1] if __name__ == '__main__': ft = Fruit() for item in ft: print(item) """ 这个是__next__ 方法 apple 这个是__next__ 方法 peach 这个是__next__ 方法 banana 这个是__next__ 方法 grape 这个是__next__ 方法 """
for 迭代迭代器时,会自动调用_ _next_ _()方法。自己写的那个算个假迭代器,不会执行_ _next_ _().
在实际使用中,直接继承迭代器即可。
总结:
①迭代器的_ _next_ _(),记住迭代的位置
②一次占用一个数的内存,省内存
③for原理:for循环自动调用_ _iter_ _(),而 _ _iter_ _()会调用 _ _next_ _(). ----------------核心
三、生成器
因为迭代器手写比较麻烦,所以python提供了生成器对象。
生成器对象的目的就是获得迭代器对象。
生成器的目的:就是获得迭代器对象(高效)
1.如何创建一个生成器对象?----使用yield
yield关键字----特殊功能,为了生成器而提供的关键字。
即函数中使用yield,调用函数的时候,会先 生成一个生成器(而不是直接运行函数)
❤生成器是特殊的迭代器,迭代器是特殊的可迭代对象。(生成器是可迭代对象)
def fun(): print("在yield1前面") yield 1 print("在yield1后面,yield2前面") yield 2 if __name__ == '__main__': # 验证是否是生成器 print(fun()) # <generator object fun at 0x104d00150> 结果是一个生成器对象,但是没有运行函数 print(hasattr(fun(),"__iter__")) # True print(hasattr(fun(),"__next__")) # True # 运行生成器,必须要调用__next__ print(fun().__next__()) # 1 / 在yield1前面 # 运行出全部结果,for自动调用__next__ for i in fun(): print(i) """ 在yield1前面 1 在yield1后面,yield2前面 2 """
①当python解释器看到函数中有yield的时候,不会立即去调用这个函数,而是先创建一个生成器对象。而这个生成器对象会记住函数的状态(存代码,记住函数的执行状态:是否执行?执行到哪里了?)。
②原理解释:在for循环中,运行到第一个yield时,就暂时退出了(类似return,但区别是return是永久跳出不会再回去了)。再次运行时(__next__记住位置),会从yield1后面开始运行。
③ 当执行next(生成器对象)时,会执行生成器对象的yield.
h = fun() # h是一个生成器对象
next(h),会执行g里面的yield
④直接调用函数,函数没有直接运行,而是先创建一个生成器对象?那什么时候才能运行yield呢?
❤调用_ _next_ _()方法是会运行yield。如上面例子中运行的for,for会自动调用_ _next_ _()。或者fun()._ _next_ _() ;
⑤生成器 就是 迭代器的抽象
⑥生成器的使用场景:协程
读写大文件
(一边下载video 一边播放。下载的内容用yield 返回,记录下载返回的数据状态)
⑦生成器 省 内存的原理:就是因为__next__
生成器是为了快速获得含有_ _next_ _()和_ _iter_ _()方法 ----就是为了获得迭代器。
summary:
1.可迭代对象(含有__iter__)
2.迭代器的特点:
记住遍历位置
迭代器只能被迭代一遍
同时含有__next__() 和__iter__()
3.迭代器的应用场景:for循环
4.有了循环为啥还要迭代器?循环需要判断便利到哪里,但是迭代器不需要判断
5.什么对象可以转化为迭代器?可迭代对象,用iter()进行转化
6.生成器(特殊的迭代器):
---简化迭代器创建过程,提供关键字yield
---函数中有yield,调用函数时,函数会先创建一个生成器对象,这个对象就是一个迭代器
---yield 记住执行的位置;保存状态、恢复状态