我有以下数据:
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]
答案 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]]
如果indices
和size
很大,您可以使用带有生成器表达式的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
理解)。对于较大的输入(我只是将indices
和size
乘以100),operator.itemgetter
以3倍的倍数获胜(切片仍然击败islice
,但是通过无意义的边距; islice
中的开销主要是在设置中,并且随着切片项目数量的增加而不会有意义地增加。)
所有相当于:
selected_names = [names[i] for i in indices][:size]
除非他们不首先填充完整的list
,然后将其缩减为size
;他们得到足够的条目并立即停止。