对列表进行逆枚举的最Pythonic方法是什么?

问题描述 投票:0回答:2

第一个想到的是:

>>> l = list('abcdef')
>>> for i in range(len(l)-1, -1, -1):
...   item = l[i]
...   print(i, item)
...
5 f
4 e
3 d
2 c
1 b
0 a

我尝试使用以下内容:

>>> l
['a', 'b', 'c', 'd', 'e', 'f']
>>> for i,ch in reversed(enumerate(l)):
...   print(i,ch)
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'enumerate' object is not reversible

但显然“枚举”对象是不可逆的。我可以用以下方法欺骗它:

>>> for i,ch in reversed(list(enumerate(l))):
...   print(i,ch)
...
5 f
4 e
3 d
2 c
1 b
0 a

但这感觉不太对——有点麻烦。有一个更好的方法吗?也许一些

inverse_enumerate
隐藏的是像集合或 itertools 这样的库?

python reverse enumerate
2个回答
5
投票

这可能不是最Pythonic的方法,但您可以重用enumerate

上的文档提供的代码来重新实现您自己的反向
enumerate

该文档提供了以下代码:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

这里是一个

renumerate
函数的提案,它以相反的顺序生成序列的元素:

def renumerate(sequence, start=None):
    n = start
    if start is None:
        n = len(sequence) - 1
    for elem in sequence[::-1]:
        yield n, elem
        n -= 1

不幸的是,这不适用于生成器,因为它需要知道序列的长度。


1
投票

我不确定 pythonic 的定义,但您可以执行以下操作(按复杂性递增的顺序):

TLDR:采取 3

为了进行比较,我还列出了使用 Gnu time 的最大内存占用量 (M) 以及使用 jupyters 内置函数 %timeit -n 4 -r 4 的计算时间 (

T
)。

参考尺寸

/usr/bin/time -v python3 -c 'list( int(1e6) * "abcdef" )'

结果为 61.4 MB。


1

正如之前所建议的:

reversed( list( enumerate(l) ) )

enumerate
返回一个生成器,并且生成器不可逆。

因此,

list
通过迭代整个生成器在内部创建一个新列表(直到抛出
StopIteration
异常),并且
reversed
以相反的顺序沿着该列表创建一个生成器。

这很简单,可能是“Pythonic”,但会产生大量的内存开销(在复制期间以及由于复制本身)。

M T(每次循环的毫秒数 [4 次运行的平均值 ± 标准差,每次 4 次循环])
673.6 MB 1.04秒±24.9

2

正如我的评论中所写,您也可以使用

sorted( enumerate(), reverse=True )

这比上面的稍微清晰一些,并且作用几乎相同。

sorted
还在内部创建一个具有相关内存开销的副本,但另外还有一个排序操作。 由于对已排序列表进行排序相当快,因此运行时开销很小。

所以你在这里用可读性来换取运行时。

M T(每次循环的毫秒数 [4 次运行的平均值 ± 标准差,每次 4 次循环])
673.6 MB 1.29秒±64毫秒

3

对于你所问的问题,据我所知,没有内置的生成器或链接生成器的方法。但你可以轻松地自己写一个,这就是我推荐的:

def enumerate_reversed(l):
    for i in zip(range(len(l)-1, -1, -1), reversed(l)):
        yield i

或者作为一句台词:

enumerate_reversed = lambda l: (x for x in zip(range(len(l)-1, -1, -1), reversed(l)))

reversed
range
zip
都是返回发电机。 这样您就可以获得倒置的
enumerate
,而无需创建任何中间副本。

M T(每次循环的毫秒数 [4 次运行的平均值 ± 标准差,每次 4 次循环])
61.7 MB 1.26 秒 ± 17.3 毫秒

附录:

使用的命令:

方法 命令
1
/usr/bin/time -f "%M" python3 -c 'reversed(list(enumerate(list(int(1e6)*"abcdef"))))'
2
/usr/bin/time -f "%M" python3 -c 'sorted(enumerate(list(int(1e6)*"abcdef")),reverse=True)'
3
/usr/bin/time -f "%M" python3 -c 'f = lambda l: (x for x in zip(range(len(l)-1, -1, -1), reversed(l))); f(list(int(1e6)*"abcdef"))'
© www.soinside.com 2019 - 2024. All rights reserved.