生成器:generator,在Python中, 一边循环一边计算的机制, 称为生成器: generator
Python2很多列表的地方,在Python3被更新为生成器
可以基于生成器直接创建列表
filter
map
1、如何创建生成器?
1.1 g1 = (x for x in range(10) if x % 2 == 0)
1.2 g2 = func1()
函数func1中必须包含yield关键字
2、如何生成数据?(下蛋的鸡)
特征:
2.1 必须先生第一个,才能生第二个
2.2 必须是一个一个来
生成语法:
a.next(g)
b.g.__next__()
c.g.send(value)
value 在生成器开始的时候,只能给None
后续可以给随意参数
如果超出生成数据范围,会导致异常 StopIteration
3、生成器的好处?
3.1 时间开销
3.2 内存开销
def generator(): print("a") count = yield 1 print("---->",count) print('b') yield 2 g = generator() print(next(g)) res = g.send('123') print(res) print("- -"*10) lis = [1,2,3,4,5,6,7] lis1 = [2,3,4,5,6,7,8] li = list(map(lambda x,y:x,lis,lis1)) for i in li: print(i) g2 = (x for x in range(10) if x % 2 == 0) print(type(g2)) # 3种使用方式 v = next(g2) print(v) v = g2.__next__() print(v) v = g2.send(None) print(v) import time import sys """ 1M = 1024kb 1kb = 1024byte 1byte = 8bit """ start = time.time() # list1 = [x for x in range(10000000)] list1 = (x for x in range(10000000)) size = sys.getsizeof(list1)#字节 end = time.time() print(f'耗时:{end-start}秒') print(f'list1占用内存{size/1024}kb') # print(f'list1占用内存{size/1024/1024}M')
运行结果
迭代器
- 迭代器: 拥有__iter__方法和__next__方法的对象就是迭代器 注意:生成器就是迭代器;可迭代对象不一定是迭代器
- 可迭代性的元素:list, tuple, str, dict, set
-
itertion
: 就是迭代
,一个接一个(one after another),是一个通用的概念,比如一个循环遍历某个数组。 -
iterable
: 这个是可迭代对象
,属于python的名词,范围也很广,可重复迭代,满足如下其中之一的都是iterable
:-
可以
for
循环:for i in iterable
-
可以按
index
索引的对象,也就是定义了__getitem__
方法,比如list,str
; -
定义了
__iter__
方法。可以随意返回。 -
可以调用
iter(obj)
的对象,并且返回一个iterator
-
-
iterator
:迭代器对象
,也属于python的名词,只能迭代一次。需要满足如下的迭代器协议
-
定义了
__iter__
方法,但是必须返回自身 -
定义了
next
方法,在python3.x是__next__
。用来返回下一个值,并且当没有数据了,抛出StopIteration
-
可以保持当前的状态
-
判断是否具有可迭代性(满足可迭代协议):
(1)查看是否实现了__iter__方法
"__iter__" in dir(可迭代性对象)
(2)判断目标对象是否是可迭代性类实例,使用 isinstance(Iterable)来判断
from collections.abc import Iterable
isinstance(可迭代性对象, Iterable)
for循环的本质
- 通过__iter__()获取该对象的一个迭代器对象
- 通过__next__()函数,依次获取下一个元素
class MyRange: def __init__(self, stop): self.start = 0 self.stop = stop self.step = 1 def __iter__(self): while self.start < self.stop: yield self.start self.start += self.step for i in MyRange(5): print(i)
迭代器的几个创建方法:
1.生成器函数
和普通函数的 return 返回不同,生成器函数使用 yield。
>>> def odd_func(start=1, end=10): ... for val in range(start, end + 1): ... if val % 2 == 1: ... yield val ... >>> of = odd_func(1, 5) >>> of <generator object odd_func at 0x101a14200> >>> iter(of) <generator object odd_func at 0x101a14200> >>> next(of) 1 >>> next(of) 3 >>> next(of) 5 >>> next(of) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
2.生成器表达式
>>> g = (v for v in range(1, 5 + 1) if v % 2 == 1) >>> g <generator object <genexpr> at 0x101a142b0> >>> iter(g) <generator object <genexpr> at 0x101a142b0> >>> next(g) 1 >>> next(g) 3 >>> next(g) 5 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
3.自定义迭代器
基于模拟Range实现的IntegRange,封装一个类,可以通过for循环直接打印指定范围的质数
# 例如:
# for i in IntegerRange(2,20,1):
# print(i)
# 可以依次打印出2,3,5,7,11...等全为质数的数字,
import math class IntegerRange: def __init__(self, start, end, step): self.start = start self.end = end self.step = step def __iter__(self): return self def __next__(self): while self.start < self.end: self.start += self.step if self.prime(self.start): return self.start else: raise StopIteration("跑完了") def prime(self, n): if n <= 1: return 0 for i in range(2, math.ceil(math.sqrt(n + 1))): if n % i == 0: return 0 return 1 g = IntegerRange(2,20,1) print(g.__next__()) print(next(g))
怎么选择?
到现在为止,我们知道了创建迭代器的 3 种方式,那么该如何选择?
最简单的就是生成器表达式,如果表达式能满足需求,那么就是它;如果需要添加比较复杂的逻辑就选生成器函数;如果前两者没法满足需求,那就自定义类实现吧。总之,选择最简单的方式就行。
注意:迭代器遍历完一次就不能从头开始了
>>> l = [1, 3, 5] >>> li = iter(l) >>> li <list_iterator object at 0x101a1da90> >>> 3 in li True >>> 3 in li False
因为 li 是列表迭代器,第一次查找 3 的时候,找到了,所以返回 True,但是由于第一次迭代,已经跳过了 3 那个元素,第二次就找不到了,所以会出现 False。
因此,记得迭代器是「一次性」的。
总结:
- 实现了迭代器协议的对象都是迭代器
- 实现了
__iter__()
方法并返回迭代器的对象是可迭代对象 - 生成器也是一种迭代器
- 创建迭代器有三种方式,生成器表达式、生成器函数、自定义类,看情况选择最简单的就好
- 迭代器同时也是可迭代对象
- 迭代器是「一次性」的