迭代(Iteration)
当我们使⽤⼀个循环来遍历某个东西时,这就叫⼀个迭代。
可迭代对象(Iterable)
⼀个可迭代对象是Python中任意的对象,只要它定义了可以返回⼀个迭代器的__iter__
⽅法,或者定义了可以⽀持下标索引的__getitem__
⽅法。简单说,⼀个可迭代对象,就是任意的对象,只要它能给我们提供⼀个迭代器。
container__iter__()
返回一个迭代器对象。 该对象需要支持迭代器协议。 如果容器支持不同的迭代类型,则可以提供额外的方法来专门地请求不同迭代类型的迭代器。
迭代器(Iterator)
⼀个迭代器是任意⼀个对象,只要它定义了⼀个next(Python2) 或者__next__
⽅法。
iterator.__next__()
从容器中返回下一项。 如果已经没有项可返回,则会引发 StopIteration
异常。
特点: 节省内存,惰性机制,不能反复, 只能向下执行.
- 迭代器是可迭代的,所有的迭代器都是它们自己的迭代器
- 迭代器没有长度,它们不能被索引
- 可以把迭代器看作是惰性迭代器,它们是一次性使用,这意味着它们只能循环遍历一次。
文件迭代器
#手动遍历 f = open('xxx','r',encoding='utf8') f.readline() #每次调用readine方法,就会到下一列,最后一行使用得到空字符 #使用f.__next__()可以达到同样的效果,不同的是,已达到最后一行,继续使用__next__会引发StopIteration异常,捕获即可解决。 #文件对象本身就是一个迭代器
#常用for循环迭代,调用内部的__next__ for i f: xxxxx
列表
列表以及很多其他的内置对象,不是自身的迭代器,所以支持多次打开迭代器。
L = [1,2,3] L.__next__() #AttributeError: 'list' object has no attribute '__next__' lst = [1,2,3] lst_iter = lst.__iter__() #while循环+迭代器模拟登录 while True: try: i = lst_iter.__next__() print(i) except StopIteration: break L = [1,2,3] a = L.__getitem__(2) # 支持下标索引,L[2] print(a) # 3
range
内置函数range,返回一个可迭代对象,根据需要产生范围中的数字,而不是在内存中构建一个结果列表
>>> range(0,7) range(0, 7) >>> r = range(0,7) >>> r[3] 3 >>> len(r) 7 >>> list(range(0,7)) #强制转换成列表 [0, 1, 2, 3, 4, 5, 6]
map、zip和filter
map、zip和filter和range不同,它们都是自己的迭代器。range支持len和索引,而它们不支持。
>>> M <map object at 0x0401A870> >>> M.__next__() 1 >>> N = M >>> N.__next__() 0
自定义可迭代对象
构建一个自定义容器对象,
class Node: def __init__(self,*value): self._children = list(value) def __iter__(self): return iter(self._children) N = Node(1,2,3,4,5) for i in N: print(i,end = ' ') #1 2 3 4 5 # #只需要定义一个__iter__方法,将迭代操作代理到容器内部的对象上 去。
生成器
生成器本质就是迭代器,生成器的两种构建方式:1.生成器函数 2.生成器表达式。
- 生成器函数:编写常规的def语句,使用yield语句返回一个结果,在每个结果之间挂起和继续它们的状态
- 生成器表达式:返回一个按需产生结果的一个对象,而不是构建一个结果列表。
二者都不会一次性构建一个列表,节省了内存空间,将计算事件分散到各个结果请求。
生成器函数
def gensquares(n): for i in range(n): yield i ** 2 def gensquares(n): for i in range(n): yield i ** 2 g = gensquares(3) print(g) #<generator object gensquares at 0x03982B30> print(g.__next__()) print(g.__next__()) # 0 1
send和next
调用g.send(value)发送给生成器g,并且生成器中的yield表达式返回了为了发生而传入的值。
def gen(): for i in range(5): x = yield i print(x) G = gen() g = G.__next__() print(g) G.send(9) # 必须先调用__next__() g = G.__next__() print(g) #结果: 0 9 None #x接收send发送的值,没有发送默认为None 2
生成器表达式
G = (x*2 +1 for x in range(4)) G.__next__()
def add(a, b): return a + b def test(): for r_i in range(4): yield r_i g = test() for n in [2, 10]: g = (add(n, i) for i in g) print(list(g)) #[20,21,22,23] 不要局部地关注for in g, (add(n, i) for i in g)这个表达式整体是一个生成器 并没有调用__next__方法。for每循环一次,n被重新赋值,并对g(表达式左边)重新赋值,生成器表达式随之发生改变。 第一次 n = 2,g = (add(n, i) for i in text()) 第二次 n = 3,g = (add(n, i) for i in ((add(n, i) for i in text()))) .......... 循环结束时 n = 10 g = (add(n, i) for i in ((add(n, i) for i in text())))