28 мая 2009 г.

Мысли по поводу list comprehension

List comprehension — приятная штука, позволяющая во многих ситуациях выразить мысль не только коротко, но и понятно. И всё же иногда кажется, что там чего-то не хватает. Вот несколько вариантов записи одного выражения:
[target for target in [long_expr_of(source) for source in sources]
 if test_expr_of(target)]

filter(lambda x: test_expr_of(x),
       [long_expr_of(source) for source in sources])

[long_expr_of(source) for source in sources
 if test_expr_of(long_expr_of(source))]
Первый способ громоздок, да и читаются вложенные списки не так хорошо. Второй способ выглядит неплохо, только если есть готовая функция test_expr_of и нет необходимости писать lambda-выражение. Третий способ неприятен с точки зрения эффективности. В таких случаях я предпочитаю разбивать вычисление на два выражения:
targets = [long_expr_of(source) for source in sources]
targets = [target for target in targets if test_expr_of(target)]
Вроде всё замечательно и ничего больше не нужно. И всё-таки, вспоминая SQL, хочется записать что-то вроде:
[long_expr_of(source) as target for source in sources
 if test_expr_of(target)]
Выглядит очень приятно. Может стоит написать PEP?

6 комментариев:

Alexander Solovyov комментирует...

Хмм... Ну не знаю, первый вариант (или последний) был бы нормальный, только вложенный lisp comprehension надо бы заменить на генераторное выражение. ;)

Unknown комментирует...

Ну от использования generator comprehension вместо list comprehension суть вопроса не меняется. Вероятно, ради такой мелочи вряд ли стоит дополнительно замусоривать язык.

Alexander Solovyov комментирует...

Ум, забыл подписаться на комментарии.

Я даже не знаю, если б такую штуку можно было еще где-то использовать... Впрочем, я часто отказываюсь от LC/GE именно из-за этого прикола. ;) Так что может и проканает. :)

bw комментирует...

Я делаю так.

> [long_expr_of(source) for source in sources if test_expr_of(long_expr_of(source))]

filter(test_expr_of, map(long_expr_of, sources))

Вообще я как-то стал больше склоняться к использованию map/reduce/filter. Совместно с operator и curry.

Зацените:

set(reduce(
__operator.iadd,
__map(operator.attrgetter('parsers'), sources),
__[]))

И это далеко не всё :-).

..bw

Unknown комментирует...

Я имел ввиду, что test_expr_of и long_expr_of являются выражениями (отсюда expr в названиях), то есть в map-reduce их придётся записывать через lambda. Согласитесь, читать код с вызовами map/reduce и громоздкими выражениями в lambda уже не так приятно.

bw комментирует...

Теперь понятно. Да, лямбда далеко не везде смотрится на своем месте.
В зависимости от размера и мудрености выражения его бы следовало вынести наружу и дать "человеческое" имя, а дальше использовать как угодно.

..bw