迭代器

迭代是python最强大的功能之一,是访问集合元素的一种方式。

  • 迭代器是一个可以记住遍历位置的对象。
  • 迭代器从第一个元素开始访问,直到所有元素访问完结束
  • 迭代器只能往前不能往后

    可迭代对象

    我们一直list、tuple、str等可以被for … in … 这样的循环语句从其中依次拿出元素使用,这种遍历过程被称为迭代

但是,不是所有的数据类型都可以放到for … in … 里取出一条数据,供我们迭代使用的,例如整型:

  1. for i in 100:
  2. print(i)

如何判断对象是否可迭代

使用 isinstance()判断一个对象是否是Iterable对象:

  1. from collections.abc import Iterable
  2. list1 = [1, 2, 3, 4]
  3. int1 = 100
  4. print(isinstance(list1, Iterable))
  5. print(isinstance(int1, Iterable))
  6. # 运行结果
  7. True
  8. False

可迭代对象的本质

分析可迭代对象的迭代过程,每迭代一次就会就会返回可迭代对象的下一条数据,一直向后读取数据知道数据读完,那么这个过程中就应该有一个“人”帮我们记录每一次访问到了第几条数据,以方便每次迭代都可以访问下一条数据。我们把这个记录迭代位置的“人”称为迭代器

可迭代对象通过iter()方法来提供一个迭代器,,我们在迭代一个可迭代对象时,实际上是先获取该对象提供的迭代器,再通过迭代器依次获取对象中的每一个数据。

也就是说,具备iter()方法的对象就是可迭代对象

可迭代协议就是可以被迭代要满足的要求:内部含有iter()方法。

  1. list1 = [1, 2, 3, 4]
  2. tuple1 = (1, 2, 3, 4)
  3. dict1 = {1: 2, 3: 4}
  4. print(dir(list1))
  5. print(dir(tuple1))
  6. print(dir(dict1))
  7. # 运行结果
  8. ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
  9. ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']
  10. ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

迭代器的本质

迭代器遵循迭代器协议,即必须拥有iter方法和next方法。
list、tuple等可迭代对象,我们可以通过调用他们的iter方法来获取这些对象的迭代器,然后对获取到的迭代器不断使用next()函数获取下一条数据。当取完所有数据后就停止迭代。

  1. list1 = [1, 2, 3, 4]
  2. l_iter = list1.__iter__()
  3. ret1 = l_iter.__next__()
  4. print(ret1)
  5. ret2 = l_iter.__next__()
  6. print(ret2)
  7. ret3 = l_iter.__next__()
  8. print(ret3)
  9. ret4 = l_iter.__next__()
  10. print(ret4)
  11. ret5 = l_iter.__next__()
  12. print(ret5)
  13. # 运行结果
  14. Traceback (most recent call last):
  15. File "C:/Users/mi/PycharmProjects/untitled/迭代器.py", line 23, in <module>
  16. ret = l_iter.__next__()
  17. StopIteration
  18. 1
  19. 2
  20. 3
  21. 4

for 循环,能遍历一个可迭代对象,它的内部到底经历了什么呢?

  1. 调用 可迭代对象.iter() 获取迭代器
  2. 不断调用 迭代器.next() 获取下一条数据
  3. 增加异常处理功能,取完数据后自动停止迭代。 ```python l = [1, 2, 3, 4]

while True: try: liter = l.iter() item = liter.__next() print(item) except StopIteration: break

  1. 通过上面的分析我们知道了迭代器是用来帮我们记录每次迭代位置的,当我们对迭代器使用__next__()方法时,迭代器就会向我们返回它所记录位置的下一个位置的数据。<br />注意迭代器本身也是可迭代的,因为它有__iter__()方法,__iter__会返回一个迭代器,迭代器自身就是迭代器,所以迭代器的__iter__返回它自身。
  2. <a name="VDRcf"></a>
  3. ### 如何判断一个对象是否是迭代器
  4. ```python
  5. from collections.abc import Iterator
  6. item1 = isinstance([], Iterator)
  7. print(item1)
  8. item2 = isinstance("abd", Iterator)
  9. print(item2)
  10. item3 = isinstance(iter("abc"), Iterator)
  11. print(item3)
  12. # 运行结果
  13. False
  14. False
  15. True

生成器

生成器定义

python提供的生成器

  1. 生成器函数:常规函数定义,但是以yield语句而不是return语句返回结果。yield一次只返回一个结果,然后挂起生成器,下次唤醒生成器从断开的地方继续执行。
  2. 生成器表达式:类似于列表推导式,但是生成器返回按需产生结果的一个对象,而不是一次构建一个列表。

    生成器 Generator

  • 本质:迭代器,所以自带iter方法和next方法
  • 特点:惰性运算,开发者自定义

    生成器函数

    一个包含 yield 关键字的函数就是一个生成器函数。
    调用生成器函数不是返回一个具体的值,而是返回一个可迭代对象,每一次获取这个可迭代对象的值就能推动函数执行,获取新的返回值,知道函数执行结束。

生成器的好处是不会在内存中一下子生成太多数据。
比如说生产10000个包子不需要一次性做完,可以让包子在我需要的时候再被生产出来。

  1. # 生产包子(生成器)
  2. def produce():
  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. print('=========================')
  10. num = 0
  11. for i in produce_g:
  12. print(i)
  13. num += 1
  14. if num == 5:
  15. break

总结

  • 使用yield关键字的函数不再是函数,而是生成器。
  • yield的作用
    • 保存函数当前的运行状态(断点),然后暂停执行,将生成器挂起
    • 将yield关键字后面的值作为返回值返回,这里可以理解起到了return的作用
  • 唤醒生成器:通过next方法使生成器从断点处继续执行

    send函数

    send()获取下一个值的效果与next基本一致,但是载获取下一个值的同时会给上一个yield的位置传递一个数据。
    使用send的注意点:

  • 第一次使用生成器要使用next来获取下一个值

  • 最后一个yield不能接收外部的值 ```python

    send函数

    def generator(): print(123) content = yield 1 print(“==============”, content) print(456) yield 2

g = generator() ret = g.next() # ret 用来接收函数返回值1,此时执行到第一个yield挂起生成器 print(‘返回值’, ret) ret = g.send(‘hello’) # ret 用来接收函数返回值2,send函数唤醒生成器并把’hello’赋值给上一个yield print(‘返回值’, ret)

  1. <a name="ChKMW"></a>
  2. # 列表推导式
  3. 例题<br />1. 过滤掉长度小于3的字符串列表,并将剩下的转换成大写字母<br />2. 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
  4. ```python
  5. # 练习一
  6. list1 = ['abd', 'a', 'bc', 'hello world']
  7. list2 = [s.upper() for s in list1 if len(s) >= 3]
  8. print(list2)
  9. # 运行结果
  10. ['ABD', 'HELLO WORLD']
  11. # 练习二
  12. list3 = [(x, y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 != 0]
  13. print(list3)
  14. # 运行结果
  15. [(0, 1), (0, 3), (0, 5), (2, 1), (2, 3), (2, 5), (4, 1), (4, 3), (4, 5)]