Python 以自定义步长或向下枚举

26 投票
6 回答
22677 浏览
提问于 2025-04-18 10:14

如何让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的生成器和迭代器有什么区别?

撰写回答