python全栈闯关--13-迭代器和生成器
1、迭代器
可迭代协议
含有双下划线方法__iter__()方法的都是可迭代
迭代器协议
内部含有__next__和__iter__方法的就是迭代器
# 判断是否函数__iter__方法 print("__iter__" in dir(int)) print("__iter__" in dir(bool)) print("__iter__" in dir(list)) print("__iter__" in dir(dict)) print("__iter__" in dir(set)) print("__iter__" in dir(tuple)) print("__iter__" in dir(enumerate([]))) print("__iter__" in dir(range(1)))
print([]) print([].__iter__()) # 使用__iter__方法后,返回值为一个迭代器 print(set(dir([].__iter__())) - set(dir([]))) # {'__setstate__', '__length_hint__', '__next__'} print([1, 2, 'aas', 'a'].__iter__().__length_hint__()) # 返回元素的个数 # 迭代器使用next方法,可以遍历值 l = [1, 2, 3, 4] iterator = l.__iter__() print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__()) # 迭代器返回值完毕后,程序报错“StopIteration”
Iterable 可迭代的 -- > __iter__ 只要含有__iter__方法的都是可迭代的
[].__iter__() 迭代器 -- > __next__ 通过next就可以从迭代器中一个一个的取值
迭代器协议和可迭代协议
- 可以被for循环的都是可迭代的
- 可迭代的内部都有__iter__方法
- 只要是迭代器,就一定可迭代
- 可迭代的__iter__()可以得到一个迭代器
- 迭代器中的__next__方法可以一个一个的获取值
迭代器的好处
- 从容器中可以一个一个的取值,能取到所有值
- 节省空间
- 迭代器不会因此在内存中开辟一大块空间,而是随着循环,每次通过next方法,生成一个值
print(range(100000000000000)) print(list(range(1000000000)))
运行上述代码,可以看到,第一个迭代器的时候,很快就返回了结果 range(0, 100000000000000)
第二段代码,机子跑死机了,都无法正常运行
2、生成器函数
含有yiled关键字的函数都是生成器函数
#正常函数 def generaor(): print('1') return 'a' ret=generaor() print(ret)
还有yiled关键字时,不能含有return关键字
def generaor(): print('1') yield 'a' # 替换return关键字未yield关键字后,就是一个生成器 ret=generaor() print(ret)
def generaor(): print('1') yield 'a' # 替换return关键字未yield关键字后,就是一个生成器 ret=generaor() print(ret) print("next get value:", ret.__next__()) # 生成器使用next函数获取值
生成器中,使用双下线方法来获取返回值
含有多个yield时,每次使用__next__都是到yield时,停止执行,返回值。
单生成器中的值返回完毕时,报错StopIteration
def generaor(): print('1') yield 'a' # 替换return关键字未yield关键字后,就是一个生成器 print('2') yield 'b' yield 'c' ret=generaor() print(ret) print("next get value:", ret.__next__()) # 生成器使用next函数获取值 # 1 # next get value: a print("next get value:", ret.__next__()) # 生成器使用next函数获取值 # 2 # next get value: b print("next get value:", ret.__next__()) # 生成器使用next函数获取值 # next get value: c # print("next get value:", ret.__next__()) # 生成器使用next函数获取值 # 报错StopIteration,生成器到达最后的值报错
同时定义多个生成器时,多个生成器间并不矛盾,每个都能获取各自独立的值
def bear(): for i in range(20000): yield "bear %s" % i g = bear() g2 = bear() print('生成器函数1:%s' % g.__next__()) print('生成器函数1:%s' % g.__next__()) print('生成器函数2:%s' % g2.__next__()) print('生成器函数2:%s' % g2.__next__()) print('生成器函数1:%s' % g.__next__())
总结:
迭代器:
- 双下方法:很少直接调用的方法,一般情况下,通过其他语法触发;
- 可迭代的:可迭代协议,含有__iter__方法('__iter__' in dir(类型))
- 可迭代的一定可以被for循环;
- 迭代器协议:含有__iter__和__next__的方法。
- 迭代器一定可以迭代,可迭代的通过__iter__方法可以得到一个迭代器
- 迭代器的特点:
- 可以方便的遍历,且每个元素之使用一次;
- 节省内存;
生成器:
生成器的本质是一个迭代器
- 生成器函数:
- 含有yield关键字的函数就是生成器:
- 特点
- 调用函数,并不会立即执行,返回一个生成器
- 每次调用,使用__next__获取一个值
- 直到取完最后一个值,报错
从生成器中取数的几个方法:
- for
- __next__
- 数据类型强制转换,暂用内存
练习:
def tail(filename): with open(filename) as f: while True: line = f.readline() line = line.strip() if line: yield line res = tail('file') for line in res: if 'bear' in line: print('***', line)
def generator(): for i in range(2000000000): yield "hello, bear %s" % i g = generator() print(g.__next__()) n = 0 for content in g: n += 1 if n > 50: break print(content)