Ваш второй цикл в порядке, но ваш первый цикл можно заставить работать без создания временного list
, просто используя выражение генератора :
for elt in (lst[idx] for idx in idxs):
elt.do_stuff()
elt.do_more_stuff()
или (возможно,немного быстрее, если есть много индексов) (ab?) с использованием map
:
for elt in map(lst.__getitem__, idxs):
elt.do_stuff()
elt.do_more_stuff()
В обоих случаях (по крайней мере, для Py3, где map
возвращает итератор, а не новый list
) эффект заключается в том, чтобы лениво искать каждый индекс при запросе следующего elt
;он не с готовностью набирает list
до начала цикла.
Есть еще один вариант, который вы могли бы рассмотреть, если вы собираетесь повторно искать один и тот же набор индексов (то есть idxs
не меняется).Вы можете создать operator.itemgetter
один раз и использовать его.Он будет работать с энтузиазмом (например, понимание list
), но он будет:
- Возвращать
tuple
вместо list
(немного более эффективный в использовании памяти и с лучшей локализацией памяти, но обычноне имеет существенного значения) - Протолкните здание упомянутого
tuple
, начните заканчивать, вниз к слою C, где понимание list
, при использовании специализированных байт-кодов, все еще должно выполнять всю свою работув обычном интерпретаторе, который, по крайней мере, в CPython медленнее, чем большая часть работы, переданная в C
Для этого подхода вы должны сделать:
# Done once up front
from operator import itemgetter
getidxs = itemgetter(*idxs) # Note: Will fail if idxs is not at least length 2; won't return tuple when getting one item
# Done every time
for elt in getidxs(lst):
elt.do_stuff()
elt.do_more_stuff()
Вам необходимопрофиль, чтобы определить:
- нужна ли какая-либо из этих оптимизаций
- Какое решение наиболее целесообразно для вас (такие решения, как
itemgetter
и listcomp, используют больше памяти, номожет работать быстрее, ленивые решения имеют фиксированные и небольшие накладные расходы памяти, но могут работать на много медленнее)