Чи Pythonic використовувати розуміння списку лише для побічних ефектів?


108

Подумайте про функцію, яку я закликаю до її побічних ефектів, а не до повернення значень (наприклад, друк на екран, оновлення графічного інтерфейсу, друк у файл тощо).

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)

Що краще і чому?


6
це є межею, але ви, мабуть, отримаєте більше проти, ніж на підтримку. Я збираюся просидіти це: ^)
jcomeau_ictx

6
Це простий вибір. Читання рахується - зробіть це по-другому. Якщо ви не можете помістити дві додаткові лінії на екрані, отримайте більший монітор :)
Джон Ла Руй

1
Розуміння списку є непітонічним, оскільки порушує "явне краще, ніж неявне" - ви ховаєте цикл у іншій конструкції.
Фред Фоо

3
@larsmans: якби тільки GvR зрозумів це, коли представив перелік розумінь у першу чергу!
Стів Джессоп

2
@larsmans, Стів Джессоп, я думаю, що неправильно сприймати розуміння списку як цикл. Він цілком може бути реалізований як цикл, але суть таких конструкцій полягає в тому, щоб функціонувати на сукупних даних функціонально та (концептуально) паралельно. Якщо є синтаксис, проблема for ... inвикористовується в обох випадках - це призводить до таких питань!
senderle

Відповіді:


84

Це дуже антипітонічно, і будь-який досвідчений піфоніст зіб'є вас з цим. Проміжний список викидається після його створення, і він потенційно може бути дуже, дуже великим, а значить, і дорогим для створення.


5
То який би був більш пітонічний спосіб?
Йоахім Зауер

6
Той, який не веде список навколо; тобто якийсь варіант другого способу (мені було відомо, що я використовував генекс forраніше, щоб позбутися від цього if).
Ігнасіо Васкес-Абрамс

6
@Joachim Sauer: Приклад 2 вище. Правильний, явний цикл розуміння, який не має списку. Явне. Ясно. Очевидно.
С.Лотт

31

Не слід використовувати розуміння списку , оскільки, як люди сказали, це створить великий тимчасовий список, який вам не потрібен. Наступні два методи еквівалентні:

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)

Звичайно, останнє зрозуміліше і простіше зрозуміти.


@Paul: Я думаю, так і має бути. Дійсно, ви можете, хоч mapі не бути настільки інтуїтивно зрозумілими, якщо раніше не робили функціонального програмування.
Катріель

4
Не впевнений, що це особливо ідіоматично. Немає переваги перед використанням явного циклу.
Марцін

1
Рішенняconsume = collections.deque(maxlen=0).extend
PaulMcG

24

Означення списків призначені для створення списків. І якщо ви не на самому справі створення списку, ви повинні НЕ використовувати спискові.

Тож я отримав би другий варіант, просто повторивши список і потім зателефонувавши до функції, коли застосовуються умови.


6
Я б пішов ще далі і констатую, що побічні ефекти в розумінні списку незвичайні, несподівані і, отже, злі, навіть якщо ви використовуєте отриманий список, коли закінчите.
Марк Рансом

11

Друге - краще.

Подумайте про людину, якій потрібно було б зрозуміти ваш код. Ви можете легко отримати погану карму з першим :)

Ви можете пройти посередині між цими двома, скориставшись 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)

10
Ваша лямбда набагато краще написана як lambda x : x > 3.
PaulMcG

Вам навіть не потрібен фільтр. Просто помістіть вираз генератора в круглих дужках тут: for el in (x for x in y if x > 3):. elі xможе мати те саме ім’я, але це може бентежити людей.
Всезнайко

3

Залежить від вашої мети.

Якщо ви намагаєтесь виконати деяку операцію з кожним об’єктом у списку, слід застосувати другий підхід.

Якщо ви намагаєтесь генерувати список з іншого списку, ви можете використовувати розуміння списку.

Явне краще, ніж неявне. Просте - краще, ніж складне. (Пітон Дзен)


0

Ви можете зробити

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

але це не дуже красиво.


-1

Використання розуміння списку для його побічних ефектів є некрасивим, непітонічним, неефективним, і я б цього не робив. Я б використав 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форма, можливо, дещо чіткіша, хоча я думаю, що обидва вони заплутані і їх не слід використовувати.

Я думаю, що це некрасиво, і я насправді цього не зробив би в коді. Але якщо ви наполягаєте на впровадженні циклів таким чином, я б це зробив.

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


Що робити, якщо fun_with_side_effectsповертає True?
Катріель

7
Я думаю, що це вилікування гірше від захворювання - itertools.consume набагато чистіше.
PaulMcG

@PaulMcG - itertools.consumeбільше не існує, ймовірно, тому, що використовувати розуміння із побічними ефектами некрасиво.
Омніфаріоз

1
Виявляється, я помилився, і це ніколи не існувало як метод у stdlib. Це є рецептом в itertools документи: docs.python.org/3/library / ...
PaulMcG
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.