Знайте, когда не следует использовать составные части списков

Понимание списков, бесспорно, самая мощная функция Python. Они питонические, мощные и удобочитаемые.

Но это легко открывает двери для их неправильного использования. Вернее, злоупотребления в различных формах. При этом ваш код становится непифоническим и его трудно расшифровать.

В следующем разделе мы рассмотрим несколько неправильных способов использования составных частей списков Python с их заменами.

Когда вы меняете только состояния, а не список

Многие программисты, начинающие работать с Python, начинают использовать составные части списков там, где в этом нет необходимости.

Например, в следующих примерах использование составления списков - не лучшая идея, поскольку мы ничего не делаем со списком.

vehicles = [ car, bike, truck ]
[ v.setLights(true) for v in vehicles ]
names = [ "A", "B", "C" ]
[ print(i) for n in names ]

В таких случаях лучше использовать классический цикл for, так как он предотвратит создание ненужного списка. Понимание списков не предназначено для указания команд или установки состояний элементов.

Когда вы просто меняете ценности

Возможно, вы захотите преобразовать символы строки или просто добавить кучу чисел. В любом из случаев понимание списка выбора просто потому, что оно причудливо, не означает, что оно тоже питоническое.

Рассмотрим следующий пример:

total = 0
listOne = [1,2,3]
[total := total + x for x in listOne]

Хотя оператор присваивания Python 3.8 := позволяет нам суммировать элементы списка, используя понимание списка, зачем изобретать колесо, если Python предоставляет встроенную функцию sum()? С пониманием приведенного выше списка можно избавиться с помощью очень короткого фрагмента кода:

sum(listOne)

Точно так же, когда вы только разбиваете строку на символы (или преобразуете их), использование списка является излишним и совсем не обязательно:

str = "Fred"
chars = [x for x in str]
#use this instead:
chars = list(str)

Когда понимание вложено в огромную логику

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

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

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

list = [[1,2,3],[-1,-5,6]]
flatten = [item
           for sublist in list
           for item in sublist
           if item > 0]
#using loops
flatten1 = []
for rows in list:
    for sublist in rows:
        if sublist > 0:
           flatten1.append(sublist)

Когда вы загружаете в память весь список, хотя он и не нужен

Понимание списков позволяет легко создавать списки, но они занимают память на все время. Это делает их очень неэффективными, особенно при работе с большими наборами данных.

Рассмотрим приведенный ниже пример, в котором вы легко можете столкнуться с ошибками нехватки памяти.

sum([i * i for i in range(1000)])

Вместо этого рекомендуется использовать выражение генератора, поскольку оно дает значение по одному:

sum(i* i for i in range(1000))

Другой пример, когда хранение списка в памяти - плохая идея, - это когда вы ищете только один элемент на основе определенного условия. Следующий однострочный список неэффективен.

first = [x for x in list 
         if x.get("id")=="12"]

Мы можем заменить это не только выражением генератора, но также установить функцию next, чтобы возвращать, когда первый элемент соответствует заданному условию:

first = next((x for x in list if x.get("id")=="12"), None)

Использование функций itertools вместе с выражением генератора - это эффективный способ выхода, а не использование понимания списка, которое не поддерживает break или continue.

Когда вы злоупотребляете составными частями списков

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

В приведенном ниже примере просто восстанавливается еще один неверный вариант использования, когда мы разделяем значения кортежа на вложенные списки:

myList = [('A', 1), ('B', 2), ('C', 3)]
result = [[ i for i, j in myList ],
       [ j for i, j in myList ]]

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

result = list(zip(*myList))
# [['A', 'B', 'C'], [1, 2, 3]]

Заключение

Компоненты списков Python обладают мощным потенциалом, но ими можно легко злоупотреблять, что делает код непитоническим.

Я надеюсь, что приведенные выше неправильные подходы помогут вам лучше понять, когда не следует использовать понимание списков.

Вот и все. Спасибо за прочтение.