Можно ли получить возвращенное значение из списка Python для использования условия? - PullRequest
7 голосов
/ 06 января 2012

Я хочу построить значение в понимании списка, но также и фильтровать это значение.Например:

[expensive_function(x) for x in generator where expensive_function(x) < 5]

Я хочу не вызывать expensive_function дважды за итерацию.

generator может возвращать бесконечный ряд, и списочные значения не лениво оцениваются.Так что это не сработает:

[y in [expensive_function(x) for x in generator where expensive_function(x)] where y < 5]

Я мог бы написать это по-другому, но для понимания списка это кажется правильным, и я уверен, что это обычный шаблон использования (возможно или нет!).

Ответы [ 4 ]

10 голосов
/ 06 января 2012

Если generator может быть бесконечным, вы не хотите использовать понимание списка. И не все должны быть однострочными.

def filtered_gen(gen):
    for item in gen:
        result = expensive_function(item)
        if result < 5:
            yield result
2 голосов
/ 27 марта 2012

Я собираюсь ответить на часть вопроса о том, как собрать промежуточные результаты в понимании списка для использования в условии, и игнорировать вопрос о понимании списка, построенного из бесконечного генератора (который, очевидно, не собираетсяна работу), на тот случай, если кто-нибудь, кто ищет ответ на вопрос в названии, придет сюда.

Итак, у вас есть такое понимание списка:

[expensive_function(x) for x in xrange(5) if expensive_function(x) % 2 == 0]

И вы хотитеСтарайтесь не вычислять expensive_function дважды, когда он проходит через ваш фильтр.Языки с более выразительным синтаксисом понимания (Scala, Haskell и т. Д.) Позволяют вам просто присваивать имена выражениям, вычисляемым из переменных понимания, что позволяет вам делать такие вещи, как:

# NOT REAL PYTHON
[result for x in xrange(5) for result = expensive_function(x) if result % 2 == 0]

Но вы можете легко эмулировать этопревращая присвоение result = expensive_function(x) в другую for итерацию по последовательности одного элемента:

[result for x in xrange(5) for result in (expensive_function(x),) if result % 2 == 0]

И доказательство:

>>> def expensive_function(x):
        print 'expensive_function({})'.format(x)
        return x + 10
>>> [expensive_function(x) for x in xrange(5) if expensive_function(x) % 2 == 0]
expensive_function(0)
expensive_function(0)
expensive_function(1)
expensive_function(2)
expensive_function(2)
expensive_function(3)
expensive_function(4)
expensive_function(4)
[10, 12, 14]
>>> [result for x in xrange(5) for result in (expensive_function(x),) if result % 2 == 0]
expensive_function(0)
expensive_function(1)
expensive_function(2)
expensive_function(3)
expensive_function(4)
[10, 12, 14]
2 голосов
/ 06 января 2012

Вы должны сделать 2 выражения генератора:

ys_all = (expensive(x) for x in xs)
ys_filtered = (y for y in ys_all if y <5)

или

from itertools import imap, ifilter
ys = ifilter(lambda y : y < 5, imap(expensive, xs))
1 голос
/ 06 января 2012

Предупреждение Это немного запутанно, но делает свою работу.Я буду использовать пример, чтобы объяснить это.

Допустим, expensive_function = math.sin

infinite generator = collections.count(0.1,0.1)

тогда

[z for z in (y if y < 5 else next(iter([])) 
     for y in (math.sin(x) for x in itertools.count(0.1,0.1)))]

равно

[0.09983341664682815,
 0.19866933079506122,
 0.2955202066613396,
 0.3894183423086505,
 0.479425538604203]

Итак, ваша проблема сводится к

[z for z in (y if y < 0.5 else next(iter([])) \
         for y in (expensive_function(x) for x in generator))]

Хитрость заключается в том, чтобы заставить StopIteration из генератора и ничего элегантного, чем next(iter([]))

Здесь expensive_functionвызывается только один раз за итерацию.

Расширение бесконечного генератора конечным генератором с условием остановки.Поскольку генератор не допускает raise StopIteration, мы выбираем извилистый способ, то есть next(iter([])) И теперь у вас есть конечный генератор, который можно использовать в понимании списка

Поскольку OP занимался приложениемописанного выше метода для немонотонной функции здесь вымышленная немонотонная функция

дорогая немонотонная функция f(x) = random.randint(1,100)*x

условие останова = < 7

[z for z in (y if y < 7 else next(iter([])) for y in 
      (random.randint(1,10)*x for x in itertools.count(0.1,0.1)))]

[0.9,
 0.6000000000000001,
 1.8000000000000003,
 4.0,
 0.5,
 6.0,
 4.8999999999999995,
 3.1999999999999997,
 3.5999999999999996,
 5.999999999999999]

Кстати: sin в истинном смысле не является монотонным во всем диапазоне (0,2pi)

...