Является ли Pythonic использование списков для только побочных эффектов?

Подумайте о функции, которую я вызываю для нее побочные эффекты, а не возвращают значения (например, печать на экран, обновление GUI, печать в файл и т.д.).

def fun_with_side_effects(x):
    ...side effects...
    return y

Теперь, это Pythonic, чтобы использовать списки, чтобы вызвать этот func:

[fun_with_side_effects(x) for x in y if (...conditions...)]

Обратите внимание, что я не сохраняю список в любом месте

Или мне следует называть это func следующим образом:

for x in y:
    if (...conditions...):
        fun_with_side_effects(x)

Что лучше и почему?

Ответ 1

Это очень анти-Pythonic, и любой опытный Pythonista даст вам черт возьми. Промежуточный список отбрасывается после его создания, и он потенциально может быть очень, очень большим и, следовательно, дорогостоящим для создания.

Ответ 2

Вы не должны использовать понимание списка, потому что, как сказали люди, вы создадите большой временный список, который вам не нужен. Следующие два метода эквивалентны:

consume(side_effects(x) for x in xs)

for x in xs:
    side_effects(x)

с определением consume на странице itertools man:

def consume(iterator, n=None):
    "Advance the iterator n-steps ahead. If n is none, consume entirely."
    # Use functions that consume iterators at C speed.
    if n is None:
        # feed the entire iterator into a zero-length deque
        collections.deque(iterator, maxlen=0)
    else:
        # advance to the empty slice starting at position n
        next(islice(iterator, n, n), None)

Конечно, последнее понятно и понятнее.

Ответ 3

Смысл списка заключается в создании списков. И если вы на самом деле не создаете список, вы не должны использовать списки.

Итак, я получил бы второй вариант, просто перейдя по списку, а затем вызовет функцию, когда применяются условия.

Ответ 4

Во-вторых, лучше.

Подумайте о человеке, который должен понимать ваш код. Вы можете получить плохую карму легко с первой:)

Вы можете пойти посредине между ними, используя filter(). Рассмотрим пример:

y=[1,2,3,4,5,6]
def func(x):
    print "call with %r"%x

for x in filter(lambda x: x>3, y):
    func(x)

Ответ 5

Зависит от вашей цели.

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

Если вы пытаетесь создать список из другого списка, вы можете использовать понимание списка.

Явный лучше, чем неявный. Простой лучше, чем сложный. (Python Zen)

Ответ 6

Вы можете сделать

for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass

но это не очень красиво.

Ответ 7

Я бы сделал это вместо этого:

any(fun_with_side_effects(x) and False for x in y if (...conditions...))

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

Я склонен чувствовать, что понимание списков и их образ должны сигнализировать о попытке использовать что-то, по крайней мере слабо напоминающее функциональный стиль. Помещение вещей с побочными эффектами, которые нарушают это предположение, заставит людей читать ваш код более тщательно, и я думаю, что это плохо.