前言:
一、先了解一下可迭代对象和迭代器的区别:
1、可迭代对象:我们常见的list,dict,str都是可迭代对象,其底层内部都实现了iter方法,一般通过for循环去取值
2、迭代器:底层实现了iter和next方法即可称为迭代器,它底层其实继承了可迭代对象Iterable类,从而继承了Iterable类内部的iter方法,然后在其本身子类中实现了next方法;其访问值的方法可通过next(迭代器对象),也可通过for i in 迭代器对象去取值
3、如何将可迭代对象变成迭代器呢?
a=[1,2,3,4,5]
b=iter(a) # 通过内置函数iter()将其变为迭代器
第一种取值方法:
print(next(b)) # 输出:1
print(next(b)) # 输出:2
.....
若超出列表个数则抛出StopIteration
第二种取值方法:
for i in b:
print(i)
备注:第二种方法不会抛出异常,for内部自己处理了StopIteration异常
4、从底层再看可迭代对象和迭代器
from collections.abc import Iterator, Iterable
print('可迭代对象底层方法', Iterable.__dict__)
print('迭代器底层方法', Iterator.__dict__)
print('迭代器父类', Iterator.__base__)
输出结果:
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>}
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>}
3、迭代器父类 <class 'collections.abc.Iterable'>
上面输出的结果1中对象中实现了iter方法,2中实现了iter方法和next方法,再看3中,通过打印其父类可以看到迭代器实际继承了Iterable,然后自身实现了next方法
二、yield和yield form用法
概念介绍:任何一个函数中只要实现了yield,这个函数就是生成器,生成器是一种特殊的迭代器,当程序执行到yield处会暂停,将yield后面的值返回出来,yield from也同样如此,那俩者有何区别呢?
yield:
def add(num):
yield num
----------参数传入非可迭代对象类型
ob = add(1) # 这时候是一个生成器,传入的是一个int类型
for i in ob:
print(i) # 输出结果:1
---------- 传入list类型
ob1 = add([1,2,3,4])
for i in ob1:
print(i) # 输出结果:[1,2,3,4]
yield from:
def add(num):
yield from num
----------参数传入非可迭代对象类型
ob = add(1) # 这时候是一个生成器,传入的是一个int类型
for i in ob:
print(i) # 报错:'int' object is not iterable(可迭代对象)
---------- 传入list类型
ob1 = add([1,2,3,4])
for i in ob1:
print(i) # 输出结果:1
# 输出结果:2
# 输出结果:3
# 输出结果:4
结论:如果你函数实现了yield关键字,后面可以跟任何类型的参数,返回出来的就是该参数本身,而yield from后面只能跟可迭代对象数据,它能将可迭代对象中的每个元素返回出来,所以上面yield函数中传入list数据返回出来的就是一整个 list,通过for去遍历该函数生成器对象去取值后可看到是一整个list值,而yield from函数中传入list后,通过for去遍历 该生成器对象拿到的是里面每个元素
三、yield from只仅仅局限于以上用法吗?
python中的协程想必听过,它的底层逻辑就是基于yield from的特性来实现的,yiled from可以在俩个函数之间建立双向通道,举个例子:实现一个主方法传入一串数字,子方法实时去根据个数计算平均值
# 子生成器
def calculate_average():
count = 0
avg = 0
total = 0
while True:
x = yield avg
if x is None:
break
count += 1
total += x
avg = total / count
print(f'每条数据的平均值:{avg}')
print('所有数据统计完毕!')
return avg
# 委托生成器
def gener():
while True:
calcu_value = yield from calculate_average()
print(f'平均值:{calcu_value}')
# 主方法
def main(num: list):
a = gener()
a.send(None) # 预激,或者使用next(a)去预激
for i in num:
a.send(i)
a.send(None)
输出结果为:
每条数据的平均值:1.0
每条数据的平均值:1.5
每条数据的平均值:2.0
每条数据的平均值:2.5
所有数据统计完毕!
平均值:2.5