for...in...循环执行过程
for...in...本质:调用可迭代对象的__iter__方法,获取这个方法的返回值(返回值是一个带有__next__方法的对象),然后不断调用这个对象的__next__方法。因此__next__方法需要想办法中止循环
from collections.abc import Iterable,Iterator class A: def __next__(self): return 'hello' class B: def __init__(self,x): self.x = x def __iter__(self): return A() a= A() b=B(1) print(isinstance(a,Iterable))#False print(isinstance(b,Iterable))#True print(isinstance(b,Iterator))#False """ hello hello hello ........ """ for i in b: print(i)
__iter__方法也可以返回自身self,此时该对象必须实现__next__方法:
from collections.abc import Iterable,Iterator class B: def __init__(self,x): self.x = x def __iter__(self): return self def __next__(self): return 'hello' b=B(1) print(isinstance(b,Iterable))#True print(isinstance(b,Iterator))#True """ hello hello hello ........ """ for i in b: print(i)
此时对象b既是可迭代的对象又是迭代器对象
from collections.abc import Iterable,Iterator class B: def __init__(self,x): self.num =0 self.x = x def __iter__(self): return self def __next__(self): if self.num < len(self.x): nt = self.x[self.num] else: raise StopIteration self.num += 1 return nt b=B([1,23,'zs','sd']) print(isinstance(b,Iterable))#True print(isinstance(b,Iterator))#True """ hello hello hello ........ """ for i in b: print(i)
在__next__方法中返回可以迭代的元素,也是for...in..遍历的元素。需要中止无限循环,抛出raise StopIteration异常
自己写range函数:
class My_range: def __init__(self,x): self.x = x self.count = 0 def __iter__(self): return self def __next__(self): self.count += 1 if self.count <= self.x: return self.count else: raise StopIteration for i in My_range(10): print(i) ”“” 1 2 3 4 5 6 7 8 9 10 “”“
__iter__方法,获取这个方法的返回值(返回值是一个带有__next__方法的对象),然后不断调用这个对象的__next__方法,获得返回值
- __iter__ : 返回迭代器本身,实际上相当于 return self,以便在应该使用可迭代对象的地方使用迭代器。例如在for循环中。
- __next__ : 返回下一个可用元素,如果没有元素了,则抛出StopIteration异常,决定了迭代器的迭代规则。
__iter__() 只会被调用一次,而 _next_() 会被调用 n 次,直到出现StopIteration异常。
一、迭代器
python中两个迭代的概念,一个叫做迭代器(Iterator),一个叫做可迭代对象(Iterable)
当我们实现了迭代器之后,就可以使用for循环进行遍历了。我们平常使用的字符串,列表,元组和字典等,底层都实现了迭代器。我们可以通过
instance
来判断from collections.abc import Iterable,Iterator s = "abcdefgh" print(isinstance(s,Iterable)) # True print(isinstance(s,Iterator)) # False l = [1,2,3,4,5,6,7,8] print(isinstance(s,Iterable)) # True print(isinstance(s,Iterator)) # False t = (1,2,3,4,5,6,7,8) print(isinstance(s,Iterable)) # True print(isinstance(s,Iterator)) # False
发现字符串,列表和元组并不是迭代器,而是迭代对象。
1.1、迭代器介绍
迭代器的实现非常简单,只需要实现__iter__和__next__这两个魔法函数即可,
- 调用迭代器对象的 __iter__方法得到还是迭代器对象本身,就跟没调用一样
- 调用迭代器对象的__next__方法返回下一个值,不依赖索引
- 可以一直调用__next__直到取干净,最后抛出异常StopIteration(停止迭代)
迭代器对象从集合中的第一个元素开始访问,直到所有的元素被访问完。
迭代器有两个方法:iter()和next()方法。
iter(可迭代对象):可以获得一个可迭代对象的迭代器
next(迭代器):可以获得迭代器对象的元素
class My_range: def __init__(self,x): self.x = x self.count = 0 def __iter__(self): return self def __next__(self): self.count += 1 if self.count <= self.x: return self.count else: raise StopIteration m = My_range(5) #m实现了__iter__方法,可迭代对象 i = iter(m) #获得可迭代对象m的迭代器 print(next(i))#1 print(next(i))#2 print(next(i))#3 print(next(i))#4 print(next(i))#5
其中:
#获得可迭代对象m的迭代器
i = iter(m) 相当于m.__iter__()----->返回的是self本身
next(i) 相当于m.__next__() ----->返回的是__next__()返回值。每次循环都会访问调用
1.2、可迭代的对象
类似于list、tuple、str 等类型的数据可以使用for …… in…… 的循环遍历语法从其中依次拿到数据并进行使用,我们把这个过程称为遍历,也称迭代。python中可迭代的对象有list(列表)、tuple(元组)、dirt(字典)、str(字符串)set等。
mylist = [1,2,3,4,5] mydirt = { "name":"张三", "sex":"男", "age":18 } mytuple = (1,2,3,4,5) myset = {1,2,3,3,4} for i in mylist: print(i) for i in mytuple: print(i) for i in myset: print(i) for i,j in mydirt.items(): print("%s:%s" % (i,j))
除此之外,也可以创建一个可迭代的对象:只要此对象含有__iter__方法,那么它就是一个可迭代的对象,如下面的例子:
其中定义了一个__iter__方法,我们通过isinstance()函数以及Iterable来判断由Classmate类创建的class1对象是否是可迭代的对象:若class1是一个Iterable(可迭代对象)则结果返回为True;否则,结果为False。
from collections import Iterable class Classmate(object): """定义一个同学类""" def __init__(self): self.name = list() self.name_num = 0 def add(self,name): self.name.append(name) def __iter__(self): pass class1 = Classmate() class1.add("张三") class1.add("李四") class1.add("王五") print("判断是否是可迭代的对象:", isinstance(class1,Iterable))
1.3、创建迭代器对象
一个类(对象)只要含有“__iter__”、"__next__"两个方法,就将其称为迭代器。__iter__方法返回一个特殊的迭代器对象,而这个迭代器对象自动实现了_next__方法,并返回一个值,最后通过抛出异常StopIteration来结束迭代。我们来给上一个例子增加__next__方法:
from collections import Iterable from collections import Iterator class Classmate(object): """定义一个同学类""" def __init__(self): self.name = list() self.name_num = 0 def add(self,name): self.name.append(name) def __iter__(self): pass def __next__(self): pass class1 = Classmate() class1.add("张三") class1.add("李四") class1.add("王五") print("判断是否是可迭代的对象:", isinstance(class1,Iterable)) print("判断是否是迭代器:", isinstance(class1,Iterator))
只是名义上的 可迭代对象/迭代器 还不够,具有相应的功能才算是完整。首先,对于__iter__方法,它需要具有一个可以返回一个迭代器对象的功能(这个对象可以是自己(前提是本身就是一个迭代器),也可以是其它迭代器);
对于__next__方法,它需要标记并返回下一个迭代器对象。代码如下(为防止迭代速度过快,我们添加sleep来控制速度):
from collections import Iterable from collections import Iterator import time class Classmate(object): """定义一个同学类""" def __init__(self): self.name = list() self.name_num = 0 def add(self,name): self.name.append(name) def __iter__(self): return self # 返回本身 def __next__(self): # 记忆性返回数据 if self.name_num < len(self.name): ret = self.name[self.name_num] self.name_num += 1 return ret class1 = Classmate() class1.add("张三") class1.add("李四") class1.add("王五") print("判断是否是可迭代的对象:", isinstance(class1,Iterable)) print("判断是否是迭代器:", isinstance(class1,Iterator)) for name in class1: print(name) time.sleep(1)
iter(iterable),iterable为可迭代的对象。如iter([1, 2, 3])--->获得迭代器对象
next(iterator)
:iterator为迭代器对象。如next(iter([1, 2, 3]))
即:
iter_ obj = iter([1,2,3])
next(iter_obj)
1.4、使用迭代器实现斐波那契数列
class Fab: def __init__(self,n): self.n = n self.forward = 0 self.cur = 1 self.count = 0 def __iter__(self): return self def __next__(self): self.count += 1 if self.count <= self.n: self.forward,self.cur = self.cur,self.forward+self.cur # time.sleep(1) else: raise StopIteration return self.cur for fab in Fab(8): print(fab)
二、生成器
迭代器和生成器以及上下文管理器都是python的高级特性,它们的区别在于实例化出来的对象中是否含有一些函数。在for循环中不管是传入的可迭代对象还是迭代器对象(生成器对象)
都会先进行调用iter()方法将其变为迭代器,再进行执行next()方法,直到抛出异常
2.1、生成器介绍
为了抽象出迭代器模式,Python2.2(2001年)加入了yield关键字,来构建生成器(generator)。
在调用生成器运行的过程中,每次遇到 yield 关键字时函数会暂停并保存当前所有的运行信息,返回 yield 语句的值, 并在下一次执行 next() 方法时从当前位置继续运行,带有 yield 语句的函数不再是一个普通函数,python 解释器会将其视为一个 生成器(generator),所以只要python函数的定义体中有yield关键字,该函数就是生成器函数。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。def yield_test(): print('这一行被执行') yield 1 yield 2 run_test = yield_test() print(next(run_test)) # 输出:这一行被执行 1 print(next(run_test)) # 输出:2 print(next(run_test)) # 输出:报StopIteration异常
def fibonacci(border): a, b = 0, 1 while True: if b < border: yield b a, b = b, a + b else: break for num in fibonacci(10): print(num, end=' ') # 1 1 2 3 5 8
# 定义生成器函数 def generater_test(): print('start') yield 'A' print('continue') yield 'B' print('end') # 列表推导式 gen_list1 = [x*3 for x in generater_test()] # start # continue # end # for循环迭代gen_list1列表 for i in gen_list1: print('--->', i) # ---> AAA # ---> BBB # 生成器表达式 gen_list2 = (x*3 for x in generater_test()) # gen_list2是一个生成器对象 print(gen_list2) # <generator object <genexpr> at 0x0000016F37F90EB0> # for循环迭代gen_list2 for j in gen_list2: print('--->', j) # start # ---> AAA # continue # ---> BBB # end
2.2、 生成器函数
含有 yield 关键字的函数,调用该函数时会返回一个生成器。
可以看到,上面的函数没有使用 return 语句返回值,而是使用了 yield 返回一个值。一个带有 yield 的函数就是一个生成器函数,当我们使用 yield 时,它帮我们自动创建了__iter__() 和 next() 方法,而且在没有数据时,也会抛出 StopIteration 异常,也就是我们不费吹灰之力就获得了一个迭代器,非常简洁和高效。
2.3、带有 yield 的函数执行过程
- 调用该函数的时候不会立即执行代码,而是返回了一个生成器对象;
- 当使用 next() (在 for 循环中会自动调用 next() ) 作用于返回的生成器对象时,函数 开始执行,在遇到 yield 的时候会『暂停』,并返回当前的迭代值;
- 当再次使用 next() 的时候,函数会从原来『暂停』的地方继续执行,直到遇到 yield语 句,如果没有 yield 语句,则抛出异常;
- 整个过程看起来就是不断地 执行->中断->执行->中断 的过程。一开始,调用生成器函数的时候,函数不会立即执行,而是返回一个生成器对象;然后,当我们使用 next() 作用于它的时候,它开始执行,遇到 yield 语句的时候,执行被中断,并返回当前的迭代值,要注意的是,此刻会记住中断的位置和所有的变量值,也就是执行时的上下文环境被保留起来;当再次使用 next() 的时候,从原来中断的地方继续执行,直至遇到 yield ,如果没有 yield ,则抛出异常。简而言之,就是 next 使函数执行, yield 使函数暂停。
我们也可以写一个生成器函数或类来实现range函数相同功能:
def my_gen(n): i = 0 while i < n: i +=1 yield i #这里只是拿到一个生成器函数,并没有调用这个特殊的函数 m = my_gen(10) #当你执行next(m)时才开始调用这个函数。for循环内部执行next() #当遇到函数中的yield语句暂停执行,返回yeild语句后的值,再往下继续执行 for i in m: print(i)
斐波那契数列实现:
def fab(n): num1,num2 = 0,1 count = 0 while count<n: num1,num2 = num2,num1+num2 count += 1 yield num1 f = fab(6) for i in f: print(i)