前言:

一、先了解一下可迭代对象和迭代器的区别:

1、可迭代对象:我们常见的list,dict,str都是可迭代对象,其底层内部都实现了iter方法,一般通过for循环去取值

2、迭代器:底层实现了iternext方法即可称为迭代器,它底层其实继承了可迭代对象Iterable类,从而继承了Iterable类内部的iter方法,然后在其本身子类中实现了next方法;其访问值的方法可通过next(迭代器对象),也可通过for i in 迭代器对象去取值

3、如何将可迭代对象变成迭代器呢

  1. a=[1,2,3,4,5]
  2. b=iter(a) # 通过内置函数iter()将其变为迭代器
  3. 第一种取值方法:
  4. print(next(b)) # 输出:1
  5. print(next(b)) # 输出:2
  6. .....
  7. 若超出列表个数则抛出StopIteration
  8. 第二种取值方法:
  9. for i in b:
  10. print(i)
  11. 备注:第二种方法不会抛出异常,for内部自己处理了StopIteration异常

4、从底层再看可迭代对象和迭代器

  1. from collections.abc import Iterator, Iterable
  2. print('可迭代对象底层方法', Iterable.__dict__)
  3. print('迭代器底层方法', Iterator.__dict__)
  4. print('迭代器父类', Iterator.__base__)
  5. 输出结果:
  6. 1、可迭代对象底层方法 {'__module__': 'collections.abc', '__slots__': (), '__iter__': <function Iterable.__iter__ at 0x7fc439a81310>, '__subclasshook__': <classmethod object at 0x7fc439a7aa00>, '__class_getitem__': <classmethod object at 0x7fc439a7aa30>, '__doc__': None, '__abstractmethods__': frozenset({'__iter__'}), '_abc_impl': <_abc._abc_data object at 0x7fc439a7ef40>}
  7. 2、迭代器底层方法 {'__module__': 'collections.abc', '__slots__': (), '__next__': <function Iterator.__next__ at 0x7fc439a81430>, '__iter__': <function Iterator.__iter__ at 0x7fc439a814c0>, '__subclasshook__': <classmethod object at 0x7fc439a7aac0>, '__doc__': None, '__abstractmethods__': frozenset({'__next__'}), '_abc_impl': <_abc._abc_data object at 0x7fc439a83080>}
  8. 3、迭代器父类 <class 'collections.abc.Iterable'>

上面输出的结果1中对象中实现了iter方法,2中实现了iter方法和next方法,再看3中,通过打印其父类可以看到迭代器实际继承了Iterable,然后自身实现了next方法

二、yield和yield form用法

概念介绍:任何一个函数中只要实现了yield,这个函数就是生成器,生成器是一种特殊的迭代器,当程序执行到yield处会暂停,将yield后面的值返回出来,yield from也同样如此,那俩者有何区别呢?

yield:

  1. def add(num):
  2. yield num
  3. ----------参数传入非可迭代对象类型
  4. ob = add(1) # 这时候是一个生成器,传入的是一个int类型
  5. for i in ob:
  6. print(i) # 输出结果:1
  7. ---------- 传入list类型
  8. ob1 = add([1,2,3,4])
  9. for i in ob1:
  10. print(i) # 输出结果:[1,2,3,4]

yield from:

  1. def add(num):
  2. yield from num
  3. ----------参数传入非可迭代对象类型
  4. ob = add(1) # 这时候是一个生成器,传入的是一个int类型
  5. for i in ob:
  6. print(i) # 报错:'int' object is not iterable(可迭代对象)
  7. ---------- 传入list类型
  8. ob1 = add([1,2,3,4])
  9. for i in ob1:
  10. print(i) # 输出结果:1
  11. # 输出结果:2
  12. # 输出结果:3
  13. # 输出结果:4

结论:如果你函数实现了yield关键字,后面可以跟任何类型的参数,返回出来的就是该参数本身,而yield from后面只能跟可迭代对象数据,它能将可迭代对象中的每个元素返回出来,所以上面yield函数中传入list数据返回出来的就是一整个 list,通过for去遍历该函数生成器对象去取值后可看到是一整个list值,而yield from函数中传入list后,通过for去遍历 该生成器对象拿到的是里面每个元素

三、yield from只仅仅局限于以上用法吗?

python中的协程想必听过,它的底层逻辑就是基于yield from的特性来实现的,yiled from可以在俩个函数之间建立双向通道,举个例子:实现一个主方法传入一串数字,子方法实时去根据个数计算平均值

  1. # 子生成器
  2. def calculate_average():
  3. count = 0
  4. avg = 0
  5. total = 0
  6. while True:
  7. x = yield avg
  8. if x is None:
  9. break
  10. count += 1
  11. total += x
  12. avg = total / count
  13. print(f'每条数据的平均值:{avg}')
  14. print('所有数据统计完毕!')
  15. return avg
  16. # 委托生成器
  17. def gener():
  18. while True:
  19. calcu_value = yield from calculate_average()
  20. print(f'平均值:{calcu_value}')
  21. # 主方法
  22. def main(num: list):
  23. a = gener()
  24. a.send(None) # 预激,或者使用next(a)去预激
  25. for i in num:
  26. a.send(i)
  27. a.send(None)
  28. 输出结果为:
  29. 每条数据的平均值:1.0
  30. 每条数据的平均值:1.5
  31. 每条数据的平均值:2.0
  32. 每条数据的平均值:2.5
  33. 所有数据统计完毕!
  34. 平均值:2.5