迭代器

字符串、列表、元组、字典、集合都可以被 for 循环,说明他们都是可迭代的

  1. from collections import Iterable
  2. l = [1, 2, 3, 4]
  3. t = (1, 2, 3, 4)
  4. d = {1: 2, 3: 4}
  5. s = {1, 2, 3, 4}
  6. print(isinstance(l, Iterable))
  7. print(isinstance(t, Iterable))
  8. print(isinstance(d, Iterable))
  9. print(isinstance(s, Iterable))

可迭代协议

可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了 iter 方法。

  1. l = [1, 2, 3, 4]
  2. t = (1, 2, 3, 4)
  3. d = {1: 2, 3: 4}
  4. s = {1, 2, 3, 4}
  5. print(dir(l))
  6. print(dir(t))
  7. print(dir(d))
  8. print(dir(s))

可迭代的:内部必须含有一个 iter 方法。

迭代器

  1. l = [1, 2, 3, 4]
  2. l_iter = l.__iter__()
  3. item = l_iter.__next__()
  4. print(item)
  5. item = l_iter.__next__()
  6. print(item)
  7. item = l_iter.__next__()
  8. print(item)
  9. item = l_iter.__next__()
  10. print(item)
  11. item = l_iter.__next__()
  12. print(item)

迭代器遵循迭代器协议:必须拥有 iter 方法和 next 方法。
for 循环,能遍历一个可迭代对象,他的内部到底进行了什么?
将可迭代对象转化成迭代器。(可迭代对象.iter())
内部使用 next 方法,一个一个取值。
加了异常处理功能,取值到底后自动停止。

  1. l = [1, 2, 3, 4]
  2. l_iter = l.__iter__()
  3. while True:
  4. try:
  5. item = l_iter.__next__()
  6. print(item)
  7. except StopIteration:
  8. break

为什么要有 for 循环

for 循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的 iter 方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通过 for 循环来遍历了
最重要的一点,转化成迭代器,在循环时,同一时刻在内存中只出现一条数据,极大限度的节省了内存

生成器

初识生成器

Python 中提供的生成器

  1. 生成器函数:常规函数定义,但是,使用 yield 语句而不是 return 语句返回结果。yield 语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
  2. 生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

生成器 Generator

  • 本质:迭代器 (所以自带了iter 方法和 next 方法,不需要我们去实现)
  • 特点:惰性运算, 开发者自定义

生成器函数

一个包含 yield 关键字的函数就是一个生成器函数。yield 可以为我们从函数中返回值,但是 yield 又不同于 return,return 的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。

  1. import time
  2. def genrator_func1():
  3. a = 1
  4. print('将a赋值')
  5. yield a
  6. b = 2
  7. print('将b赋值')
  8. yield b
  9. g1 = genrator_func1()
  10. print(g1,next(g1))
  11. print(next(g1))

生成器不会一下子在内存中生成太多数据
比如我想卖包子,让包子工厂开始加工 10000 个包子,但是如果一下子全部生产好,没地方放,而且容易坏。
那么可以让包子工厂在我需要的时候再生产

  1. def produce():
  2. '''生产包子'''
  3. for i in range(10000):
  4. yield '生产了第%s个包子'%i
  5. produce_g = produce()
  6. print(produce_g.__next__())
  7. print(produce_g.__next__())
  8. print(produce_g.__next__())
  9. # 需要一批包子
  10. num = 0
  11. for i in produce_g:
  12. print(i)
  13. num += 1
  14. if num == 5:
  15. break

send

send 获取下一个值的效果和 next 基本一致
只是在获取下一个值的时候,给上一 yield 的位置传递一个数据
使用 send 的注意事项

  • 第一次使用生成器的时候 是用 next 获取下一个值
  • 最后一个 yield 不能接受外部的值
    1. def generator():
    2. print(123)
    3. content = yield 1
    4. print('=========',content)
    5. print(456)
    6. yield 2
    7. g = generator()
    8. ret = g.__next__()
    9. print('***',ret)
    10. ret = g.send('hello')
    11. print('***',ret)

列表推导式和生成器表达式

  1. l = [i for i in range(10)]
  2. print(l)
  3. l1 = ['项目%s'%i for i in range(10)]
  4. print(l1)
  1. 把列表解析的 [] 换成 () 得到的就是生成器表达式
  2. 列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
  3. Python 不但使用迭代器协议,让 for 循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum 函数是 Python 的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:
    1. ret = sum(x for x in range(101))
    2. print(ret)