#python #dictionary #rename
#python #понимание списка
Вопрос:
Подумайте о функции, которую я вызываю для ее побочных эффектов, а не возвращаемых значений (например, печать на экране, обновление графического интерфейса, печать в файл и т. Д.).
def fun_with_side_effects(x):
...side effects...
return y
Теперь, является ли Pythonic использовать понимание списка для вызова этой функции:
[fun_with_side_effects(x) for x in y if (...conditions...)]
Обратите внимание, что я нигде не сохраняю список
Или я должен называть эту функцию следующим образом:
for x in y:
if (...conditions...):
fun_with_side_effects(x)
Что лучше и почему?
Комментарии:
1. это погранично, но вы, вероятно, получите больше возражений, чем поддержки. Я собираюсь пропустить это: ^)
2. Это простой выбор. Важна читаемость — сделайте это вторым способом. Если вы не можете разместить 2 дополнительные строки на экране, получите монитор большего размера 🙂
3. @larsmans: если бы только GvR понял это, когда он впервые представил понимание списков!
4. @larsmans, Стив Джессоп, я думаю, что неверно представлять понимание списка как цикл. Это вполне может быть реализовано как цикл, но смысл подобных конструкций заключается в том, чтобы оперировать агрегированными данными функциональным и (концептуально) параллельным образом. Если есть проблема с синтаксисом, она
for ... in
используется в обоих случаях — что приводит к таким вопросам, как этот!5. @senderle: Хотя, я думаю, это зависит от побочных эффектов. Если побочные эффекты просто изменяют один элемент за раз, независимо, то я думаю, что для этого вполне разумно использовать конструкции функционального стиля на императивном языке, потому что важно не управление потоком цикла, а приложение к каждому элементу. Если побочные эффекты таковы, что порядок имеет значение, возможно, тогда абстракция «понимания» начинает просачиваться. Однако достаточно ли он протекает, чтобы иметь значение, это другой вопрос — никто не притворяется, что Python выполняет ленивую оценку.
Ответ №1:
Это очень антипитонически, и любой опытный питонист устроит вам ад из-за этого. Промежуточный список выбрасывается после его создания, и он потенциально может быть очень, очень большим и, следовательно, дорогостоящим для создания.
Ответ №2:
Вам не следует использовать понимание списка, потому что, как уже говорили люди, это приведет к созданию большого временного списка, который вам не нужен. Следующие два метода эквивалентны:
consume(side_effects(x) for x in xs)
for x in xs:
side_effects(x)
с определением consume
из itertools
справочной страницы:
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)
Конечно, последнее понятнее и понятнее.
Комментарии:
1. @Paul: Я думаю, так и должно быть. И действительно, вы можете, хотя
map
это может быть не так интуитивно понятно, если раньше не занимался функциональным программированием.2. Не уверен, что это особенно идиоматично. Нет никакого преимущества по сравнению с использованием явного цикла.
3. Решение
consume = collections.deque(maxlen=0).extend
Ответ №3:
Понимание списков предназначено для создания списков. И если вы на самом деле не создаете список, вам не следует использовать понимание списка.
Итак, я бы выбрал второй вариант, просто выполнив итерацию по списку, а затем вызвав функцию, когда применяются условия.
Комментарии:
1. Я бы пошел еще дальше и заявил, что побочные эффекты при понимании списка необычны, неожиданны и, следовательно, вредны, даже если вы используете результирующий список, когда закончите.
Ответ №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)
Комментарии:
1. Вам даже не нужен фильтр. Просто поместите выражение генератора в скобки здесь:
for el in (x for x in y if x > 3):
.el
иx
могут иметь одно и то же имя, но это может сбить людей с толку.2. Обратите внимание, что эти два
x
параметра находятся в разных областях, что является плохой практикой.
Ответ №5:
Зависит от вашей цели.
Если вы пытаетесь выполнить какую-то операцию над каждым объектом в списке, следует использовать второй подход.
Если вы пытаетесь сгенерировать список из другого списка, вы можете использовать понимание списка.
Явное лучше, чем неявное. Простое лучше, чем сложное. (Python Zen)
Комментарии:
1. В принципе, лучший ответ. Синтаксис подчиняется правилам, так что можно ответить «правильно», «неправильно», «так», «так». Стиль подчиняется другим соображениям, таким как здравый смысл, элегантность, лаконичность, эффективность, эффективность и т. Д. Это зависит от ваших целей и даже от вашего вкуса.
Ответ №6:
Вы можете сделать
for z in (fun_with_side_effects(x) for x in y if (...conditions...)): pass
но это не очень красиво.
Ответ №7:
Использование понимания списка для его побочных эффектов уродливо, непитонно, неэффективно, и я бы этого не сделал. Вместо этого я бы использовал for
цикл, потому for
что цикл сигнализирует о процедурном стиле, в котором важны побочные эффекты.
Но, если вы абсолютно настаиваете на использовании понимания списка из-за его побочных эффектов, вам следует избегать неэффективности, используя вместо этого выражение генератора. Если вы абсолютно настаиваете на этом стиле, сделайте одно из этих двух:
any(fun_with_side_effects(x) and False for x in y if (...conditions...))
или:
all(fun_with_side_effects(x) or True for x in y if (...conditions...))
Это выражения генератора, и они не генерируют случайный список, который выбрасывается. Я думаю all
, что форма, возможно, немного более понятна, хотя я думаю, что оба они сбивают с толку и не должны использоваться.
Я думаю, что это некрасиво, и я бы на самом деле не стал делать это в коде. Но если вы настаиваете на реализации своих циклов таким образом, вот как я бы это сделал.
Я склонен считать, что понимание списков и им подобных должно сигнализировать о попытке использовать что-то, хотя бы отдаленно напоминающее функциональный стиль. Использование вещей с побочными эффектами, которые нарушают это предположение, приведет к тому, что людям придется более внимательно читать ваш код, и я думаю, что это плохо.
Комментарии:
1. Что, если
fun_with_side_effects
возвращает True?2. Я думаю, что это лекарство хуже, чем болезнь — itertools.consume намного чище.
3. @PaulMcG —
itertools.consume
больше не существует, вероятно, потому, что использование понимания с побочными эффектами уродливо.4. Оказывается, я ошибся, и он никогда не существовал как метод в stdlib. Это рецепт в документах itertools: docs.python.org/3/library /…