Охват списка, (далее ОС), бесспорно, самая мощная возможность 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 :=
позволяет нам считать сумму элементов списка с помощью ОС, так зачем изобретать колесо, если язык предоставляет встроенную функцию sum()
? ОС выше заменяется удивительно коротким фрагментом кода:
Точно так же, когда вы только разбиваете строку на символы или преобразуете её, не нужно использовать ОС:
str = "Fred"
chars = [x for x in str]
#Лучше так:
chars = list(str)
ОС, каким бы хорошим он ни был, будучи вложенными, доставляет только неудобства. В массивной логике проблемы с удобочитаемостью только усугубляются. ОС в Python нужно использовать только в прозрачной логике. Можно отказаться от вложенных ОС в пользу вложенных циклов. Следующий пример демонстрирует, насколько вложенные циклы облегчают понимание:
list = [[1,2,3],[-1,-5,6]]
flatten = [item
for sublist in list
for item in sublist
if item > 0]
#Использование циклов
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
, чтобы она выполняла возврат, когда первый элемент соответствует заданному условию:
Сочетание функций 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 — мощный инструмент, но им легко злоупотребить, понизив тем самым эффективность и читаемость кода. Я надеюсь, что приведенные выше примеры неправильных подходов помогут вам лучше судить о том, когда не следует применять охват списка. Вот и всё на сегодня. Спасибо, что прочитали!
Перевод статьи Anupam Chugh: 5 Wrong Use Cases Of Python List Comprehensions
Комментарии