Python 以自定义步长或向下枚举
如何让Python的enumerate
函数从大数到小数进行计数(也就是降序、递减、倒计时)?或者说,如何在enumerate
中使用不同的步长来增加或减少计数?
举个例子,如果我们对列表['a', 'b', 'c']
使用这个函数,起始值设为10
,步长设为-2
,那么得到的结果会是[(10, 'a'), (8, 'b'), (6, 'c')]
。
6 个回答
0
如果你不需要把迭代器保存在变量里,只是想遍历某个容器,可以把你的索引乘以步长。
container = ['a', 'b', 'c']
step = -2
for index, value in enumerate(container):
print(f'{index * step}, {value}')
>>> 0, a
-2, b
-4, c
2
另一个选择是使用 itertools.count
,这个工具在按步长进行“枚举”时特别有用,尤其是反向枚举。
import itertools
counter = itertools.count(10, -2)
[(next(counter), letter) for letter in ["a", "b", "c"]]
# [(10, 'a'), (8, 'b'), (6, 'c')]
特点
- 简洁
- 步长和方向的逻辑都很紧凑地存储在
count()
中 - 枚举的索引可以通过
next()
来获取 count()
本身是无限的;如果终止的边界未知,这个特性很有用(见 @jonrsharpe)- 序列的长度本质上会终止这个无限的迭代器
3
这里有一种常见的做法:
list(zip(itertools.count(10,-2), 'abc'))
返回结果是:
[(10, 'a'), (8, 'b'), (6, 'c')]
10
一种方法是把你的可迭代对象和一个范围(range
)结合起来,使用zip
函数:
for index, item in zip(range(10, 0, -2), ['a', 'b', 'c']):
...
不过,这种方法有个限制,就是你需要知道这个范围应该有多大(也就是它至少要覆盖的最小值——就像我举的例子,多出来的部分会被zip
截断)。
如果你不知道这个范围的大小,你可以自己创建一个“无限范围”(或者直接使用itertools.count
)来实现:
>>> def inf_range(start, step):
"""Generator function to provide a never-ending range."""
while True:
yield start
start += step
>>> list(zip(inf_range(10, -2), ['a', 'b', 'c']))
[(10, 'a'), (8, 'b'), (6, 'c')]
33
我没有找到比写一个简单的生成器更优雅、更符合习惯和更简洁的方法:
def enumerate2(xs, start=0, step=1):
for x in xs:
yield (start, x)
start += step
示例:
>>> list(enumerate2([1,2,3], 5, -1))
[(5, 1), (4, 2), (3, 3)]
>>> list(enumerate2([1,2,3], 5, -2))
[(5, 1), (3, 2), (1, 3)]
如果你不理解上面的代码,可以看看 Python中的"yield"关键字是干什么的? 和 Python的生成器和迭代器有什么区别?。