Python不使用枚举就获得列表推导的当前索引

时间:2016-02-25 12:14:21

标签: python python-3.x

我有以下数据:

names = ['foo','bar','baz', 'spam', 'ham', 'jam']

indices =[0,2,3,4]

size = 3

并且想要创建索引在indices中的名称列表。该列表必须具有变量size中指定的大小。

通过这样做(错误的长度)我无法实现它:

selected_names = []
selected_names = [names[i] for i in indices if len(selected_names) <= size]
# Out[5]: ['foo', 'baz', 'spam', 'ham']

我不喜欢这个解决方案,因为在开头声明空列表并不优雅。

我可以这样做:

selected_names = [names[i] for x,i in  enumerate(indices) if x <= size]

但这有点不可读,列表长度仍然是错误的。

是否有正确且更美观的方式来创建该列表?也许是这样的?

#pseudo code
selected_names = [names[i] for i in indices if list_current_index < size]

1 个答案:

答案 0 :(得分:3)

enumerate甚至无法解决这个问题,因为当你 size元素时,它会导致你停止,而不是当你&#39;} d 保留 size元素。它似乎有用的唯一原因是您使用<= size的测试(实际上保留了size + 1个元素),而您的indices恰好是一个大于size的元素。如果indices较大或size较小,则您的测试无法按预期工作。

如果目标是保留size元素,而不处理超出需要的元素,那么最简单的方法(假设您不介意切片创建一个小的中间list,这是通常没问题)只是:

selected_names = [names[i] for i in indices[:size]]

如果indicessize很大,您可以使用带有生成器表达式的itertools.islice来避免中间切片,使用更少的内存,但更多的CPU:

import itertools

selected_names = [names[i] for i in itertools.islice(indices, size)]

我能找到的最快的选项,完全避免任何显式循环,是使用operator module,虽然它涉及到参数传递的临时性,如果size永远不会发生,这可能是一个坏主意。是巨大的(成千上万的人):

import operator

selected_names = operator.itemgetter(*indices[:size])(names)

这会创建一个itemgetter可调用来查找size中的第一个indices元素,然后立即在names上调用它,返回tuple个所有值(如果您需要可变itemgetter结果而不是list,请在list中打包tuple。它还避免了CPython中的所有Python级循环; CPython中的C层仍然会出现循环,但C层的循环比Python层的任何循环运行得快得多。对于简单的ipython %timeit测试,operator.itemgetter方法获胜,比片段+ list理解时间减少约24%(反过来比{{islice快约9% 1}} + list理解)。对于较大的输入(我只是将indicessize乘以100),operator.itemgetter以3倍的倍数获胜(切片仍然击败islice,但是通过无意义的边距; islice中的开销主要是在设置中,并且随着切片项目数量的增加而不会有意义地增加。)

所有相当于:

selected_names = [names[i] for i in indices][:size]

除非他们不首先填充完整的list,然后将其缩减为size;他们得到足够的条目并立即停止。