Подтвердить что ты не робот

Что означает "понимание списка"? Как это работает и как я могу его использовать?

У меня есть следующий код:

[x**2 for x in range(10)]

Когда я запускаю его в оболочке Python, он возвращает:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Я искал, и похоже, это называется пониманием списка, но как это работает?

4b9b3361

Ответ 1

От документации:

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


О вашем вопросе, понимание списка делает то же самое, что и следующий "простой" код Python:

>>> l = [] 
>>> for x in range(10):
...     l.append(x**2)
>>> l
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Как вы пишете его в одной строке? Хм... мы можем... возможно... использовать map() с lambda:

>>> list(map(lambda x: x**2, range(10)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Но разве не проще и проще просто использовать понимание списка?

>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

В принципе, мы можем сделать что-нибудь с x. Не только x**2. Например, запустите метод x:

>>> [x.strip() for x in ('foo\n', 'bar\n', 'baz\n')]
['foo', 'bar', 'baz']

Или используйте x как еще один аргумент функции:

>>> [int(x) for x in ('1', '2', '3')]
[1, 2, 3]

Мы также можем, например, использовать x как ключ объекта dict. Давайте посмотрим:

>>> d = {'foo': '10', 'bar': '20', 'baz': '30'}
>>> [d[x] for x in ['foo', 'baz']]
['10', '30']

Как насчет комбинации?

>>> d = {'foo': '10', 'bar': '20', 'baz': '30'}
>>> [int(d[x].rstrip('0')) for x in ['foo', 'baz']]
[1, 3]

И так далее.


Вы также можете использовать if или if...else в понимании списка. Например, вам нужны нечетные числа в range(10). Вы можете сделать:

>>> l = []
>>> for x in range(10):
...     if x%2:
...         l.append(x)
>>> l
[1, 3, 5, 7, 9]

А это слишком сложно. Как насчет следующей версии?

>>> [x for x in range(10) if x%2]
[1, 3, 5, 7, 9]

Чтобы использовать тернарное выражение if...else, вам нужно поставить if ... else ... после x, не после range(10):

>>> [i if i%2 != 0 else None for i in range(10)]
[None, 1, None, 3, None, 5, None, 7, None, 9]

Вы слышали о вложенном понимании списка? Вы можете поместить два или более for в одно понимание списка. Например:

>>> [i for x in [[1, 2, 3], [4, 5, 6]] for i in x]
[1, 2, 3, 4, 5, 6]

>>> [j for x in [[[1, 2], [3]], [[4, 5], [6]]] for i in x for j in i]
[1, 2, 3, 4, 5, 6]

Расскажите о первой части, for x in [[1, 2, 3], [4, 5, 6]], которая дает [1, 2, 3] и [4, 5, 6]. Затем for i in x дает 1, 2, 3 и 4, 5, 6.

Предупреждение: Вам всегда нужно поставить for x in [[1, 2, 3], [4, 5, 6]] перед for i in x:

>>> [j for j in x for x in [[1, 2, 3], [4, 5, 6]]]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'x' is not defined

Мы также установили понимание, выражения выражений и выражения генератора.

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

>>> {x for x in [1, 1, 2, 3, 3, 1]}
{1, 2, 3}

Это то же самое, что:

>>> set([i for i in [1, 1, 2, 3, 3, 1]])
{1, 2, 3}

A определение dict выглядит как набор, но использует {key: value for key, value in ...} или {i: i for i in ...} вместо {i for i in ...}.

Например:

>>> {i: i**2 for i in range(5)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

И он равен:

>>> d = {}
>>> for i in range(5):
...     d[i] = i**2
>>> d
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Предоставляет ли (i for i in range(5)) кортеж? Нет!, это выражение генератора. Что возвращает генератор :

>>> (i for i in range(5))
<generator object <genexpr> at 0x7f52703fbca8>

Это то же самое, что:

>>> def gen():
...     for i in range(5):
...         yield i
>>> gen()
<generator object gen at 0x7f5270380db0>

И вы можете использовать его как генератор:

>>> gen = (i for i in range(5))
>>> next(gen)
0
>>> next(gen)
1
>>> list(gen)
[2, 3, 4]
>>> next(gen)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
StopIteration

Примечание. Если вы используете понимание списка внутри функции, вам не нужно [], если эта функция может зацикливаться на генераторе. Например, sum():

>>> sum(i**2 for i in range(5))
30

Связанный (об генераторах): Понимание генераторов в Python.

Ответ 2

Есть список, словарь и множество понятий, но нет кортежей (хотя и исследуйте "генераторные выражения" ).

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

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

Они обычно состоят из:

[<output expr> <loop expr <input expr>> <optional predicate expr>]

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

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

Когда все сделано хорошо, у них высокий коэффициент удовлетворения.

Ответ 3

Если вы предпочитаете более наглядный способ выяснить, что происходит, возможно, это поможет:

# for the example in the question...

y = []
for x in range(10):
    y += [x**2]

# is equivalent to...

y = [x**2 for x in range(10)]

# for a slightly more complex example, it is useful
# to visualize  where the various x end up...

a = [1,2,3,4]
b = [3,4,5,6]
c = []

for x in a:
          if x in b:
                  c += [x]
#   \         \        /
#    \    _____\______/
#     \  /      \
#      \/        \
#      /\         \
#     /  \         \
#    /    \         \
c = [x for x in a if x in b]

print(c)

... производит вывод [3, 4]

Ответ 4

В последнее время я видел много путаницы (по другим вопросам SO и от коллег) о том, как работают списочные выражения. Небольшая часть математического образования может помочь понять, почему синтаксис таков, и что на самом деле означает понимание списка.

Синтаксис

Лучше всего рассматривать списочные понимания как предикаты для набора/коллекции, как мы это делаем в математике, используя нотацию построителя множеств. Запись на самом деле кажется мне довольно естественной, потому что я имею степень бакалавра по математике. Но забудьте обо мне, Гвидо ван Россум (изобретатель Python) имеет степень магистра по математике и имеет математическое образование.

Задать ускоренный курс для обозначения строителя

Вот (очень основы) того, как работает нотация построителя множеств:

enter image description here

Таким образом, эта запись построителя множеств представляет набор чисел, которые строго положительны (то есть [1,2,3,4,...]).

Точки замешательства

1) Фильтр предикатов в нотации построителя множеств определяет только те элементы, которые мы хотим сохранить, и предикаты понимания списка делают то же самое. Вам не нужно включать специальную логику для пропуска элементов, они опускаются, если они не включены в предикат. Пустой предикат (то есть без условия в конце) включает все элементы в данной коллекции.

2) Фильтр предикатов в нотации построителя множеств идет в конце, и аналогично в списках. (некоторые) Начинающие думают, что что-то вроде [x < 5 for x in range(10)] даст им список [0,1,2,3,4], когда на самом деле он выдает [True, True, True, True, True, False, False, False, False, False]. Мы получаем выходные данные [True, True, True, True, True, False, False, False, False, False] потому что мы попросили Python оценить x < 5 для всех элементов в range(10). Никакой предикат не подразумевает, что мы получаем все из набора (как в нотации построителя множеств).

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

НТН!