列表⽣成式
使用列表⽣成式, 可以创建列表; 但如果一个包含元素很多的列表, 占据的内存就很大;
而且这么多元素也不一定都完全使用, 这就浪费空间
generator生成器
generator生成器 (⼀边循环⼀边计算的机制), 元素都是有规律的, 是通过算法进行不断推算的, 只是使用时候会进行处理类型 range
保存了产生元素的算法 同时记录了当前游标位置(当前拿了第几个元素)
当生成器没有数据 再进行 next 会抛出StopIteration异常
列表⽣成式转为generator生成器 (第一种方法)
把最外层的中括号改为小括号
# 0~4 (包含0不包含5)的 2次方列表
a1 = [_ ** 2 for _ in range(5)] # [0, 1, 4, 9, 16]
b1 = (_ ** 2 for _ in range(5)) # <generator object <genexpr> at ...>
print(a1, b1, sep='\n')
print(a1.__sizeof__(), b1.__sizeof__()) # 内存大小
maxSize = 10
a2 = range(maxSize + 1) # 0 ~ 10 (包含10)
b2 = [_ for _ in a2] # 这是列表式 占据可能比较大的内存(如果元素比较多) 但比 range 好, 可以把元素依次拿出来再处理
c2 = (_ for _ in a2) # 生成器 generator (对象) 通过调用next函数获取值 不进行保存数据 每次调用时返回值 值的个数不定
print(a2, b2, c2, sep='\n')
print(a2.__sizeof__(), b2.__sizeof__(), c2.__sizeof__()) # 内存大小
print(len(a2), len(b2)) # 注意 生成器没有长度
遍历生成器方法 (while/for)
# 注意 while 和 for 只能选择其中一种
# while
while True:
try:
d1 = next(c2)
print('while', d1)
except StopIteration as e: # 当没有元素会报错
break
a3 = (_ for _ in range(maxSize + 1) if _ % 2 == 0) # 循环遍历+判断过滤
# for | 上面循环遍历会使游标移到最后 for的打印可能不执行
for k, _ in enumerate(a3):
print('for', str(k).zfill(2), _)
通过函数创建 (需要 函数内部使用 yield 关键字 | 使用scrapy爬虫时用过) (第二种方式)
# 以上列表生成式和生成器 个数都是有限的, 而且算法也比较简单
# 通过函数创建 (需要 函数内部使用 yield 关键字 | 使用scrapy爬虫时用过)
# 定义一个斐波那契数列函数
# 斐波那契数列 (Fibonacci sequence), 又称黄金分割数列
# 因数学家莱昂纳多·斐波那契 (Leonardo Fibonacci) 以兔子繁殖为例子而引入, 故又称为 "兔子数列"
# 在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2) (n ≥ 2,n ∈ N*)
# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34,... | 数列
# 0, 1, 2, 3, 4, 5, 6, 07, 08, 09,... | 次数
def fibonacci_1(times): # times: 次数 | 次数 >= 2 | 有限制的
i = 0
a, b = 0, 1
while i < times:
yield b # b 斐波那契数列的元素 | yield 一般用于创建生成器 把yield后面变量值返回给生成器 不是返回给函数
a, b = b, a + b # a 和 b 重新赋值, 上一层的b值赋值给当前层a, 上一层的a+b的和赋值给b
i += 1 # 层级数增加1
a4 = fibonacci_1(6) # 当函数内有 yield 就是一个生成器
print(a4)
for k, _ in enumerate(a4):
print('for', str(k).zfill(2), _)
print('-' * 80)
def fibonacci_2(): # 无限制
a, b = 0, 1
while True: # 死循环
t = yield b # b 斐波那契数列的元素 | yield 一般用于创建生成器 把yield后面变量值返回给生成器 不是返回给函数
a, b = b, a + b # a 和 b 重新赋值, 上一层的b值赋值给当前层a, 上一层的a+b的和赋值给b
print(t) # 通过 next 多次可以返回一个 None, 当前层次并不会返回 | 这也说明 yield 没返回值
a5 = fibonacci_2() # 当函数内有 yield 就是一个生成器 | 这个是无限的 别想用循环遍历 | 自然数的斐波那契数列
print(a5)
print(next(a5)) # 第1次
print(next(a5)) # 第2次 None出现在数字上面了
print(next(a5)) # 第3次 None出现在数字上面了
print(next(a5)) # 第4次 None出现在数字上面了
print(next(a5)) # 第5次 None出现在数字上面了
print(next(a5)) # 第6次 None出现在数字上面了
# 也可以这样打印
print(a5.__next__()) # 第7次 None出现在数字上面了
print(a5.__next__()) # 第8次 None出现在数字上面了
print(a5.__next__()) # 第9次 None出现在数字上面了
# 也可以这样 send 但第一个值必须 None 后面没所谓 但不能不传 | 不太好用
print(a5.send(None))
print(a5.send(0)) # 会把0也打印
print(a5.send(0))
print(a5.send(0))
print(a5.send(0))
print('end')
02.迭代器
说明
迭代是访问集合元素的⼀种⽅式.
迭代器是⼀个可以记住遍历的位置的对象. 迭代器对象从集合的第⼀个元素开始
访问, 直到所有的元素被访问完结束. 迭代器只能往前不会后退
可以使用for循环的类型有:
一类是: 如list, tuple, dict, set, str…
一类是: 生成器(上面说的 generator)
这些统称为可迭代对象: Iterable
如何判断数据是否可迭代对象??
通过 isinstance(数据, 数据类型)
from collections.abc import Iterable
def isIterable(data):
return isinstance(data, Iterable)
print(isIterable(range(5)))
print(isIterable([1, 2, 3, 4, 5]))
print(isIterable({}))
print(isIterable(tuple()))
print(isIterable((1, 2, 3, 4, 5)))
迭代器对象 Iterator
可以被next()函数调⽤并不断返回下⼀个值的对象称为迭代器
但注意 list, dict, str 都不是迭代器
这表示数据是个数据流, 未知长度, 是个有序序列,只能不断通过next() 函数实现按需计算下一个数据
计算是惰性的, 只有在需要返回下一个数据时它才会计算
from collections.abc import Iterator
def isIterator(data): # 是否迭代器对象
return isinstance(data, Iterator)
print('-' * 80)
print(isIterator(range(5)))
print(isIterator([1, 2, 3, 4, 5]))
print(isIterator({}))
print(isIterator(tuple()))
print(isIterator((1, 2, 3, 4, 5)))
这些都不是, 那些是呢?? 暂时只有生成器是
a2 = (_ for _ in range(5))
print(isIterator(a2))
把可迭代对象变成迭代器对象
在数据前加上iter
print('-' * 80)
print(isIterator(iter(range(5))))
print(isIterator(iter([1, 2, 3, 4, 5])))
print(isIterator(iter({})))
print(isIterator(iter(tuple())))
print(isIterator(iter((1, 2, 3, 4, 5))))