Ответ 1
Использование вложенного списка:
[x for x in [map_to_obj(v) for v in v_list] if x]
или еще лучше, понимание списка вокруг выражения генератора:
[x for x in (map_to_obj(v) for v in v_list) if x]
У меня есть метод, который принимает список и возвращает объект:
# input a list, returns an object
def map_to_obj(lst):
a_list = f(lst)
return a_list[0] if a_list else None
Я хочу получить список, который содержит все сопоставленные элементы, которые не являются None
.
Как это:
v_list = [v1, v2, v3, v4]
[map_to_obj(v) for v in v_list if map_to_obj(v)]
Но нехорошо вызывать метод map_to_obj
дважды в понимании списка.
Есть ли способ иметь локальные переменные в списках, чтобы он мог иметь лучшую производительность?
Или компилятор оптимизирует его автоматически?
Вот что я хочу:
(sml like)
[let mapped = map_to_obj(v) in for v in v_list if mapped end]
Использование вложенного списка:
[x for x in [map_to_obj(v) for v in v_list] if x]
или еще лучше, понимание списка вокруг выражения генератора:
[x for x in (map_to_obj(v) for v in v_list) if x]
Присвоение переменной - это просто единственное связывание:
[x for v in l for x in [v]]
Это более общий ответ, а также ближе к тому, что вы предложили. Так что для вашей проблемы вы можете написать:
[x for v in v_list for x in [map_to_obj(v)] if x]
Вы можете избежать повторного расчета с помощью встроенного python filter
:
list(filter(lambda t: t is not None, map(map_to_obj, v_list)))
Локальная переменная может быть установлена в пределах понимания путем обмана и использования дополнительного "for", которое "перебирает" 1-элементный кортеж, содержащий желаемое значение для локальной переменной. Вот решение проблемы ОП с использованием этого подхода:
[o for v in v_list for o in (map_to_obj(v),) if o]
Здесь o
- это локальная переменная, равная map_to_obj(v)
для каждого v
.
В моих тестах это немного быстрее, чем выражение вложенного генератора Lying Dog (и также быстрее, чем двойной вызов OP функции map_to_obj(v)
, который, что удивительно, может быть быстрее, чем выражение вложенного генератора, если функция map_to_obj
не слишком медленная).
Перечисление списков прекрасно подходит для простых случаев, но иногда простой старинный цикл for
является самым простым решением:
other_list = []
for v in v_list:
obj = map_to_obj(v)
if obj:
other_list.append(obj)
Теперь, если вам действительно нужен список comp и вы не хотите создавать список tmp, вы можете использовать версии итератора filter
и map
:
import itertools as it
result = list(it.ifilter(None, it.imap(map_to_obj, v_list)))
или более просто:
import itertools as it
result = filter(None, it.imap(map_to_obj, v_list)))
В версиях итератора не создается временный список, они используют ленивую оценку.
Я выяснил способ использования reduce
:
def map_and_append(lst, v):
mapped = map_to_obj(v)
if mapped is not None:
lst.append(mapped)
return lst
reduce(map_and_append, v_list, [])
Как насчет производительности этого?
Начиная с Python 3.8
и введением выражений присваивания (PEP 572) (:=
оператор), можно использовать локальную переменную в пределах понимания списка, чтобы избежать вызова дважды одной и той же функции:
В нашем случае мы можем назвать оценку map_to_obj(v)
как переменную o
, используя результат выражения для фильтрации списка; и, таким образом, используйте o
в качестве отображенного значения:
[o for v in [v1, v2, v3, v4] if (o := map_to_obj(v))]