Подтвердить что ты не робот

Как установить локальную переменную в понимании списка?

У меня есть метод, который принимает список и возвращает объект:

# 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] 
4b9b3361

Ответ 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]

Ответ 2

Присвоение переменной - это просто единственное связывание:

[x   for v in l   for x in [v]]

Это более общий ответ, а также ближе к тому, что вы предложили. Так что для вашей проблемы вы можете написать:

[x   for v in v_list   for x in [map_to_obj(v)]   if x]

Ответ 3

Вы можете избежать повторного расчета с помощью встроенного python filter:

list(filter(lambda t: t is not None, map(map_to_obj, v_list)))

Ответ 4

Локальная переменная может быть установлена в пределах понимания путем обмана и использования дополнительного "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 не слишком медленная).

Ответ 5

Перечисление списков прекрасно подходит для простых случаев, но иногда простой старинный цикл 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)))

В версиях итератора не создается временный список, они используют ленивую оценку.

Ответ 6

Я выяснил способ использования 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, [])

Как насчет производительности этого?

Ответ 7

Начиная с 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))]