Что такое list comprehension python

Когда использовать List Comprehension в Python

Перевод статьи «When to Use a List Comprehension» in Python, опубликованный сайтом webdevblog.ru.

Прим. переводчика: В русской терминологии нет общепризнанного перевода list comprehension. Гугл его переводит как списковое включение или абстракция списков. Хотя наиболее часто можно встретить фразу генератор списков, но мне кажется, это не совсем правильно, так как в Python есть отдельное понятие генератора. По-моему, наиболее подходящий перевод — представление списков. Поэтому в этой статье эта фраза будет использоваться без перевода, либо будет переводится следующим образом: list comprehension представление списков, set comprehension представление множества и dictionary comprehension представление словаря).

Содержание

Python известен тем, что позволяет нам писать элегантный, понятный и простой код. Одной из отличительных особенностей языка является list comprehension (представление списков), которое можно использовать для создания мощных функций в одной строке кода. Однако многие разработчики постоянно пытаются полностью использовать более продвинутые функции list comprehension. Некоторые программисты даже используют их слишком часто, что может привести к меньшей эффективности кода и сложности для чтения.

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

В этом уроке вы узнаете, как:

Как создаются списки в Python

Чтобы лучше понять компромиссы, связанные с использованием list comprehension, давайте сначала рассмотрим способы создания списков.

Использование цикла for

Использование объектов map()

map() предоставляет альтернативный подход, основанный на функциональном программировании. Мы передаем функцию и итерируемый объект (iterable), а map() создает объект. Этот объект содержит выходные данные, которые мы получаем при запуске каждого итерируемого элемента через предоставленную функцию.

Немного запутано, поэтому давайте рассмотрим пример. Допустим, нам надо рассчитать цену после вычета налога для списка транзакций:

Использование List Comprehensions

List comprehensions — это третий способ составления списков. При таком элегантном подходе мы можем переписать цикл for из первого примера всего в одну строку кода:

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

Каждое представление списков в Python включает три элемента:

Единственное различие между этой реализацией и map() состоит в том, что list comprehension возвращает список, а не объект map.

Преимущества использования представления списков

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

Python включает в себя простые и мощные инструменты, которые вы можете использовать в самых разных ситуациях. И именно поэтому list comprehension считаются Pythonic.

List comprehensions также более декларативны, чем циклы. Это означает, что их легче читать и понимать. Циклы требуют, чтобы вы фокусировались на создании списка. Вам нужно вручную создать пустой список, зациклить элементы и добавить каждый из них в конец списка. Используя представления списков, вы можете вместо этого сосредоточиться на том, что хотите добавить в список, и положиться на то, что Python сам позаботится о его построении.

Что такое list comprehension python. Смотреть фото Что такое list comprehension python. Смотреть картинку Что такое list comprehension python. Картинка про Что такое list comprehension python. Фото Что такое list comprehension python

Погружаемся в Comprehensions

Чтобы понять всю ценность list comprehensions, полезно понять диапазон их функционала. Мы также коснемся изменений, которые были внесены в Python 3.8.

Использование условной логики

Вы уже видели этот шаблон для создания представлений списка:

Хотя этот шаблон точен, он также немного неполон. Более полное описание шаблона добавляет поддержку необязательных условных выражений. Наиболее распространенный способ добавить условную логику к list comprehension — добавить в конец условное выражение:

Здесь наше условное утверждение предшествует закрывающей скобке.

Условные выражения позволяют отфильтровывать нежелательные значения без вызова filter() :

В этом блоке кода условный оператор отфильтровывает в sentence любые символы, не являющиеся гласными.

Условие может содержать любое допустимое выражение. Если вам нужен более сложный фильтр, вы можете даже переместить условную логику в отдельную функцию:

Здесь мы создаем сложный фильтр is_consonant() и передаем эту функцию как условный оператор для нашего представления списка. Обратите внимание, что значение элемента i также передается в качестве аргумента нашей функции.

Для простой фильтрации условие можно поместить в конец оператора. Но что, если вы хотите не отфильтровать элемент, изменить его значение? В этом случае полезно поместить условное выражение в начало выражения:

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

Использование Set и Dictionary Comprehensions

Представления списков в Python используются очень часто. Но это не единственный тип представлений. Вы также можете создавать представления множеств и словарей (set comprehension и dictionary comprehension). set comprehension почти ничем не отличается от представления списка. Разница лишь в том, что заданные значения обеспечивают, чтобы выходные данные не содержали дубликатов. Вы можете создать set comprehension, используя фигурные скобки вместо квадратных:

Dictionary comprehension похоже на set comprehension, но с дополнительным требованием определения ключа:

Использование оператора Walrus

Python 3.8 представил выражение присваивания (assignment expression), также известное как оператор walrus (оператор моржа). Чтобы понять, как он используется, рассмотрим следующий пример.

Скажем, нам нужно сделать десять запросов к API, который будет возвращать данные о температуре. Мы хотим вернуть только результаты, превышающие 100 градусов по Фаренгейту. Предположим, что каждый запрос будет возвращать разные данные. В этом случае мы не сможем использовать list comprehension для решения проблемы. Формула expression for member in iterable (if conditional) не позволяет условию назначить данные переменной, к которой может обращаться это выражение.

Оператор walrus решает эту проблему. Он позволяет нам запускать выражение, одновременно назначая выходное значение переменной. Следующий пример показывает, как это возможно, путем использования get_weather_data() для генерации поддельных данных о погоде:

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

Что такое list comprehension python. Смотреть фото Что такое list comprehension python. Смотреть картинку Что такое list comprehension python. Картинка про Что такое list comprehension python. Фото Что такое list comprehension python

Когда не использовать List Comprehension в Python

Представления списков полезны и могут помочь нам написать элегантный код, который легко читать и отлаживать. Но это не всегда является правильным выбором. Представления могут замедлить работу вашего кода или привести к большему использованию памяти. Если из-за list comprehensions код станет менее производительным или более сложным для понимания, то, вероятно, лучше выбрать альтернативный вариант.

Остерегайтесь вложенных Comprehensions

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

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

Внешнее представление списка [… for _ in range(6)] создает шесть строк, в то время как внутреннее представление списка [i for i in range(5)] заполняет каждую из этих строк значениями.

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

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

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

Хотя вложенные однострочные представления списков могут показаться более Pythonic, самое важное — это написать код, который ваша команда сможет легко понять и изменить. Выбирая свой подход, нужно ориентироваться на собственное суждение о том, не ухудшает ли представление списка читабельность вашего кода.

Используйте Генераторы для больших наборов данных

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

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

Когда размер списка становится проблематичным, зачастую полезнее использовать генератор вместо list comprehension. Генератор не создает единую большую структуру данных в памяти, а вместо этого возвращает итерацию. Ваш код будет запрашивать следующее значение из итерируемого объекта столько раз, сколько необходимо, или пока вы не достигните конца своей последовательности, сохраняя при этом только одно значение за раз.

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

То, что это — генератор, видно по тому, что выражение не заключено в квадратные или фигурные скобки. При желании генераторы могут быть заключены в круглые скобки.

Приведенный выше пример все еще требует много времени для выполнения, но программа не зависнет, так как операции выполняются отложено. Из-за отложенных вычислений значения рассчитываются только по явному запросу. После того, как генератор выдаст значение (например, 567 * 567), он может добавить это значение к текущей сумме, затем отбросить это значение и сгенерировать следующее (568 * 568). Когда функция sum запрашивает следующее значение, цикл начинается заново. Для этого процесса необходим небольшой объем памяти.

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

Профилирование для оптимизации производительности

Итак, какой подход быстрее? Стоит ли использовать списки или одну из их альтернатив? Вместо того, чтобы придерживаться единого правила на все случаи жизни, лучше спросить себя, имеет ли значение производительность в ваших конкретных обстоятельствах. Если нет, то обычно лучше выбрать подход, который приведет к чистому коду!

Здесь мы определяем три метода, каждый из которых использует свой подход для создания списка. Затем мы сообщаем timeit, что каждая из этих функций должна запускаться по 100 раз. timeit возвращает общее время, необходимое для выполнения этих 100 выполнений.

Заключение

В этом посте вы узнали, как использовать list comprehension в Python для выполнения сложных задач без чрезмерного усложнения кода.

Всякий раз, когда вам нужно выбрать метод создания списка, попробуйте несколько реализаций и подумайте, что легче всего прочитать и понять в вашем конкретном сценарии. Если важна производительность, можно использовать инструменты профилирования, которые дадут вам действенные данные.

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

Источник

Списковое включение (List comprehension) в Python

Что такое list comprehension python. Смотреть фото Что такое list comprehension python. Смотреть картинку Что такое list comprehension python. Картинка про Что такое list comprehension python. Фото Что такое list comprehension python

Что такое list comprehension python. Смотреть фото Что такое list comprehension python. Смотреть картинку Что такое list comprehension python. Картинка про Что такое list comprehension python. Фото Что такое list comprehension python

Списковое включение – это некий синтаксический сахар, позволяющий упростить генерацию последовательностей (списков, множеств, словарей, генераторов). Никто не заставляет его использовать в своем коде. Но если вы считаете себя (или стремитесь к этому) профессионалом, вы обязаны знать эту конструкцию.

Поначалу list comprehension может показаться сложным, но как только вы освоите эту возможность, то без проблем будете с ней работать.

1. Способы формирования списков

1.1. Использование цикла For

Наиболее удобным является цикл for (тем не менее, никто не мешает применять цикл while , если вам хочется лишней головной боли). Последовательность действий такова:

1.2. Использование функции map()

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

Читайте также

1.3. Использование генератора списка

Что такое list comprehension python. Смотреть фото Что такое list comprehension python. Смотреть картинку Что такое list comprehension python. Картинка про Что такое list comprehension python. Фото Что такое list comprehension python

List comprehension – элегантный способ создавать списки в стиле языка Python. Стоит один раз освоить – и вы всегда будете им пользоваться, где это уместно.

Общая структура выражения:

Здесь есть 3 элемента:

Имеется начальный список цен на изделия. Сегодня на них дана скидка 10 %. Составим новый l ist с учетом удешевления стоимости.

2. Причины использовать list comprehension

Списковые включения считаются более «пайтонообразными», нежели циклы или функция map() для генерации списков. В других популярных языках программирования вы не встретите такой конструкции.

Их можно использовать не только в качестве «создателей» списков, но и для фильтрации данных. Зачастую (но не всегда – об этом будет сказано дальше) list comprehension проще понимать и читать в чужом коде (да и в своем, когда вы вернетесь к нему через некоторое время).

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

3. Применение условий при генерировании списков

3.1. Условие в конце включения

3.2. Условие в начале включения

В отличие от предыдущего типа условий, здесь оно может дополняться вариантом else (но elif и тут невозможен).

Пример 7. Генерирование списка, в котором будут отмечены английские и неанглийские буквы

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

4. Сложные списковые включения

Читайте также

5. Ограничения на применение списка включений

Хоть генераторы списков и выглядят красивыми, когда небольшие, они начинают становиться громоздкими по мере роста «мерности» и сложности.

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

Также, list comprehension даже теоретически не везде можно применить. Это касается вопроса замены циклов со множеством условий. Например, если бы задача с буквами разных алфавитов стояла иначе (напротив каждой буквы указать язык, к которому она принадлежит: русский, английский или греческий), мы бы никак не смогли воспользоваться генераторами списков.

Источник

5. Data StructuresВ¶

This chapter describes some things you’ve learned about already in more detail, and adds some new things as well.

5.1. More on ListsВ¶

The list data type has some more methods. Here are all of the methods of list objects:

list. extend ( iterable )

Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.

Remove the item at the given position in the list, and return it. If no index is specified, a.pop() removes and returns the last item in the list. (The square brackets around the i in the method signature denote that the parameter is optional, not that you should type square brackets at that position. You will see this notation frequently in the Python Library Reference.)

Return zero-based index in the list of the first item whose value is equal to x. Raises a ValueError if there is no such item.

The optional arguments start and end are interpreted as in the slice notation and are used to limit the search to a particular subsequence of the list. The returned index is computed relative to the beginning of the full sequence rather than the start argument.

Return the number of times x appears in the list.

Sort the items of the list in place (the arguments can be used for sort customization, see sorted() for their explanation).

Reverse the elements of the list in place.

An example that uses most of the list methods:

Another thing you might notice is that not all data can be sorted or compared. For instance, [None, ‘hello’, 10] doesn’t sort because integers can’t be compared to strings and None can’t be compared to other types. Also, there are some types that don’t have a defined ordering relation. For example, 3+4j 5+7j isn’t a valid comparison.

5.1.1. Using Lists as StacksВ¶

5.1.2. Using Lists as QueuesВ¶

It is also possible to use a list as a queue, where the first element added is the first element retrieved (“first-in, first-out”); however, lists are not efficient for this purpose. While appends and pops from the end of list are fast, doing inserts or pops from the beginning of a list is slow (because all of the other elements have to be shifted by one).

To implement a queue, use collections.deque which was designed to have fast appends and pops from both ends. For example:

5.1.3. List ComprehensionsВ¶

List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.

For example, assume we want to create a list of squares, like:

Note that this creates (or overwrites) a variable named x that still exists after the loop completes. We can calculate the list of squares without any side effects using:

which is more concise and readable.

A list comprehension consists of brackets containing an expression followed by a for clause, then zero or more for or if clauses. The result will be a new list resulting from evaluating the expression in the context of the for and if clauses which follow it. For example, this listcomp combines the elements of two lists if they are not equal:

and it’s equivalent to:

Note how the order of the for and if statements is the same in both these snippets.

If the expression is a tuple (e.g. the (x, y) in the previous example), it must be parenthesized.

List comprehensions can contain complex expressions and nested functions:

5.1.4. Nested List ComprehensionsВ¶

The initial expression in a list comprehension can be any arbitrary expression, including another list comprehension.

Consider the following example of a 3×4 matrix implemented as a list of 3 lists of length 4:

The following list comprehension will transpose rows and columns:

As we saw in the previous section, the nested listcomp is evaluated in the context of the for that follows it, so this example is equivalent to:

which, in turn, is the same as:

In the real world, you should prefer built-in functions to complex flow statements. The zip() function would do a great job for this use case:

See Unpacking Argument Lists for details on the asterisk in this line.

5.2. The del statementВ¶

There is a way to remove an item from a list given its index instead of its value: the del statement. This differs from the pop() method which returns a value. The del statement can also be used to remove slices from a list or clear the entire list (which we did earlier by assignment of an empty list to the slice). For example:

del can also be used to delete entire variables:

Referencing the name a hereafter is an error (at least until another value is assigned to it). We’ll find other uses for del later.

5.3. Tuples and SequencesВ¶

We saw that lists and strings have many common properties, such as indexing and slicing operations. They are two examples of sequence data types (see Sequence Types — list, tuple, range ). Since Python is an evolving language, other sequence data types may be added. There is also another standard sequence data type: the tuple.

A tuple consists of a number of values separated by commas, for instance:

As you see, on output tuples are always enclosed in parentheses, so that nested tuples are interpreted correctly; they may be input with or without surrounding parentheses, although often parentheses are necessary anyway (if the tuple is part of a larger expression). It is not possible to assign to the individual items of a tuple, however it is possible to create tuples which contain mutable objects, such as lists.

A special problem is the construction of tuples containing 0 or 1 items: the syntax has some extra quirks to accommodate these. Empty tuples are constructed by an empty pair of parentheses; a tuple with one item is constructed by following a value with a comma (it is not sufficient to enclose a single value in parentheses). Ugly, but effective. For example:

This is called, appropriately enough, sequence unpacking and works for any sequence on the right-hand side. Sequence unpacking requires that there are as many variables on the left side of the equals sign as there are elements in the sequence. Note that multiple assignment is really just a combination of tuple packing and sequence unpacking.

5.4. SetsВ¶

Python also includes a data type for sets. A set is an unordered collection with no duplicate elements. Basic uses include membership testing and eliminating duplicate entries. Set objects also support mathematical operations like union, intersection, difference, and symmetric difference.

Here is a brief demonstration:

5.5. DictionariesВ¶

Performing list(d) on a dictionary returns a list of all the keys used in the dictionary, in insertion order (if you want it sorted, just use sorted(d) instead). To check whether a single key is in the dictionary, use the in keyword.

Here is a small example using a dictionary:

The dict() constructor builds dictionaries directly from sequences of key-value pairs:

In addition, dict comprehensions can be used to create dictionaries from arbitrary key and value expressions:

When the keys are simple strings, it is sometimes easier to specify pairs using keyword arguments:

5.6. Looping TechniquesВ¶

When looping through dictionaries, the key and corresponding value can be retrieved at the same time using the items() method.

When looping through a sequence, the position index and corresponding value can be retrieved at the same time using the enumerate() function.

To loop over two or more sequences at the same time, the entries can be paired with the zip() function.

To loop over a sequence in reverse, first specify the sequence in a forward direction and then call the reversed() function.

To loop over a sequence in sorted order, use the sorted() function which returns a new sorted list while leaving the source unaltered.

Using set() on a sequence eliminates duplicate elements. The use of sorted() in combination with set() over a sequence is an idiomatic way to loop over unique elements of the sequence in sorted order.

It is sometimes tempting to change a list while you are looping over it; however, it is often simpler and safer to create a new list instead.

5.7. More on ConditionsВ¶

The conditions used in while and if statements can contain any operators, not just comparisons.

The comparison operators in and not in check whether a value occurs (does not occur) in a sequence. The operators is and is not compare whether two objects are really the same object. All comparison operators have the same priority, which is lower than that of all numerical operators.

It is possible to assign the result of a comparison or other Boolean expression to a variable. For example,

5.8. Comparing Sequences and Other TypesВ¶

Sequence objects typically may be compared to other objects with the same sequence type. The comparison uses lexicographical ordering: first the first two items are compared, and if they differ this determines the outcome of the comparison; if they are equal, the next two items are compared, and so on, until either sequence is exhausted. If two items to be compared are themselves sequences of the same type, the lexicographical comparison is carried out recursively. If all items of two sequences compare equal, the sequences are considered equal. If one sequence is an initial sub-sequence of the other, the shorter sequence is the smaller (lesser) one. Lexicographical ordering for strings uses the Unicode code point number to order individual characters. Some examples of comparisons between sequences of the same type:

Note that comparing objects of different types with or > is legal provided that the objects have appropriate comparison methods. For example, mixed numeric types are compared according to their numeric value, so 0 equals 0.0, etc. Otherwise, rather than providing an arbitrary ordering, the interpreter will raise a TypeError exception.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *