本文为第321篇原创
我愿竭尽所能,为你带些温暖
文章 Python要点总结,我使用了100个小例子!发出后,有几个朋友问我itertools 的函数实现不太理解,问我是否能添加详细的注释,以解惑。今天我拿出2个多小时总结一个函数,一共10几行代码,并添加详细注释,希望能帮助到更多人!
这是zip的加强版函数大概实现过程,下面逐行分析每行代码含义。
from itertools import *
def zip_longest(*args, fillvalue=None):
iterators = [iter(it) for it in args]
num_active = len(iterators)
if not num_active:
return
while True:
values = []
for i, it in enumerate(iterators):
try:
value = next(it)
except StopIteration:
num_active -= 1
if not num_active:
return
iterators[i] = repeat(fillvalue)
value = fillvalue
values.append(value)
yield tuple(values)
使用zip_longest:
a = [1, 2, 3]
b = [4, 5, 6,7,8,9]
r = zip_longest(a, b, fillvalue='-')
while True:
try:
val = next(r)
print(val)
except StopIteration:
print('end')
break
from itertools import *
def zip_longest(*args, fillvalue=None):
参数args前面带星表示可变参数,支持传入多个关键字参数a, b, c, …
fillvalue=None, 表示fillvalue是位置参数,此处表示短列表的默认填充值
iterators = [iter(it) for it in args]
args
是可迭代对象,it
为其元素,iter(it)
表示转换it
为迭代类型的对象,此处因为 a=[1,2,3], b=[4,5,6,7,8,9] 所以args = ([1,2,3],[4,5,6,7,8,9]), iterators = [iter(a),iter(b)]
num_active = len(iterators)
if not num_active:
return
如果num_active为0,即*args为空,则直接返回
while True:
...
if not num_active:
return
while
和yield tuple(values)
组合后,控制着yield的继续执行后,下一行代码在values=[]
同时组成zip_longest的终止条件,只有当*args中的所有参数都扫描一遍后,zip的任务才结束。
values = []
...
yield tuple(values)
不要小瞧values置为空,它和yield这句代码遥相呼应,保证每完成yield后都会及时清空values,确保内存使用到最少。
for i, it in enumerate(iterators):
...
iterators[i] = repeat(fillvalue)
enumerate装饰后会得到一个由index和元素本身组成的tuple,举个例子:
In [1]: for i,ele in enumerate([9,4,2,5,8]):
...: print((i,ele))
...:
(0, 9)
(1, 4)
(2, 2)
(3, 5)
(4, 8)
此处使用enumerate,因后面先遍历完的iter(a),为保证和iter(b)个数对齐,需要填充fillvalue值,需要知道a的index,此处为0.
try:
value = next(it)
except StopIteration:
...
try… except是异常捕获的标准模板,next(it)第一次执行返回it的第一个元素,a=[1,2,3],所以value值为1.
num_active表示当前存活的列表个数,一旦一个列表迭代到终点,num_active立即减1
num_active -= 1
iterators[i] = repeat(fillvalue)
repeat是itertools模块内另一个函数,如果times为默认值,则表示重复无限次,此处是一个repeat的典型用法。此处稍加注意,不要以为times参数默认值为1,或者不理解为什么要无限次重复,记住这种用法。
此处,repeat返回一个无限次重复fillvalue
值的迭代器,并赋值给此时触发StopIteration
异常的列表。此处就是iter(a),令其指向repeat创建的迭代器。
value = fillvalue
values.append(value)
yield tuple(values)
第一句,是在触发StopIteration时,将默认值fillvalue赋给value,values保存住列表对齐后位置的元素,第一次for退出时,values内值为[1,4]
有些朋友不理解yield用法,简单来说它就是特殊的伪return
返回一次值;且我们再次调用next(r)时,它会再次跑到yield的下一行重新开始执行
,此处yield的下一行是values = []
a = [1, 2, 3]
b = [4, 5, 6,7,8,9]
r = zip_longest(a, b, fillvalue='-')
while True:
try:
val = next(r)
print(val)
except StopIteration:
print('end')
break
故输出如下:
(1, 4)
(2, 5)
(3, 6)
('-', 7)
('-', 8)
('-', 9)
end
from itertools import *
#参数args前面带星表示可变参数,支持传入多个关键字参数a, b, c, ...
#fillvalue=None, 表示fillvalue是位置参数,
#此处表示短列表的默认填充值
def zip_longest(*args, fillvalue=None):
#`args`是可迭代对象,`it`为其元素,`iter(it)`表示
#转换`it`为迭代类型的对象,此处因为 a=[1,2,3],
#b=[4,5,6,7,8,9] 所以args = ([1,2,3],[4,5,6,7,8,9]),
#iterators = [iter(a),iter(b)]
iterators = [iter(it) for it in args]
num_active = len(iterators)
#如果num_active为0,即*args为空,则直接返回
if not num_active:
return
#`while`和`yield tuple(values)`组合后,
#控制着yield的继续执行后,下一行代码在`values=[]`
#同时组成zip_longest的终止条件,只有当*args中的所有参数都扫描一遍后,
#zip的任务才结束。
while True:
#不要小瞧values置为空,它和yield这句代码遥相呼应,
#保证每完成yield后都会及时清空values,确保内存使用到最少。
values = []
#enumerate装饰后会得到一个由index和元素本身组成的tuple
#此处使用enumerate,因后面先遍历完的iter(a),
#为保证和iter(b)个数对齐,需要填充fillvalue值,需要知道a的index,此处为0.
for i, it in enumerate(iterators):
#try... except是异常捕获的标准模板,
#next(it)第一次执行返回it的第一个元素,a=[1,2,3],所以value值为1.
try:
value = next(it)
except StopIteration:
#num_active表示当前存活的列表个数,
#一旦某个列表迭代到终点,num_active立即减1
num_active -= 1
if not num_active:
return
#repeat返回一个无限次重复`fillvalue`值的迭代器,
#并赋值给此时触发`StopIteration`异常的列表。
#此处就是iter(a),令其指向repeat创建的迭代器。
iterators[i] = repeat(fillvalue)
#第一句,是在触发StopIteration时,
#将默认值fillvalue赋给value,
#values保存住列表对齐后位置的元素,第一次for退出时,values内值为`[1,4]`
value = fillvalue
values.append(value)
#有些朋友不理解yield用法,简单来说它就是特殊的`伪return`返回一次值;
#且我们再次调用next(r)时,它`会再次跑到yield的下一行重新开始执行`,
#此处yield的下一行是`values = []`
yield tuple(values)
以上,通过一个函数zip_longest,我们就能学到很多个Python知识点,并且真正理解它们的用法。如果觉得有帮助,欢迎在看或分享