Что означает "понимание списка"? Как это работает и как я могу его использовать?
У меня есть следующий код:
[x**2 for x in range(10)]
Когда я запускаю его в Python Shell, он возвращает:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Я искал, и кажется, это называется понимание списка, но как это работает?
4 ответа
Постижения списков обеспечивают краткий способ создания списков. Обычными приложениями являются создание новых списков, в которых каждый элемент является результатом некоторых операций, примененных к каждому члену другой последовательности или итеративных, или создание подпоследовательности тех элементов, которые удовлетворяют определенному условию.
Что касается вашего вопроса, то понимание списка делает то же самое, что и следующий "простой" код 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
S в одном списке понимания. Например:
>>> [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}
Диктовое понимание выглядит как комплексное понимание, но оно использует {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.
Есть списки, словарь и набор определений, но нет кортежей (хотя изучите "выражения генератора").
Они решают проблему, заключающуюся в том, что традиционные циклы в Python - это операторы (ничего не возвращающие), а не выражения, которые возвращают значение.
Они не являются решением каждой проблемы и могут быть переписаны как традиционные циклы. Они становятся неуклюжими, когда состояние должно поддерживаться и обновляться между итерациями.
Они обычно состоят из:
[<output expr> <loop expr <input expr>> <optional predicate expr>]
но может быть искажено многими интересными и причудливыми способами.
Они могут быть аналогичны традиционным map()
а также filter()
операции, которые все еще существуют в Python и продолжают использоваться.
Когда все сделано хорошо, они имеют высокий коэффициент удовлетворенности.
Если вы предпочитаете более наглядный способ выяснить, что происходит, возможно, это поможет:
# 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's 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]
В последнее время я видел много путаницы (по другим вопросам SO и от коллег) о том, как работают списочные выражения. Небольшая часть математического образования может помочь понять, почему синтаксис такой, и что на самом деле означает понимание списка.
Синтаксис
Лучше думать о списках как о предикатах в наборе / наборе, как мы это делаем в математике, используя нотацию построителя множеств. Запись на самом деле кажется мне довольно естественной, потому что я имею степень бакалавра по математике. Но забудьте обо мне, Гвидо ван Россум (изобретатель Python) имеет степень магистра по математике и имеет математическое образование.
Задать ускоренный курс для обозначения строителя
Вот (очень основы) того, как работает нотация построителя множеств:
Таким образом, эта запись построителя множеств представляет собой набор чисел, которые строго положительны (т.е. [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)
, Ни один предикат не подразумевает, что мы получаем все из набора (как в нотации построителя множеств).
Если вы сохраняете нотацию построителя в глубине души, когда используете списки, их немного легче проглотить.
НТН!
Вступление
Понимание списка - это декларативный способ высокого уровня для создания списка в Python. Основными преимуществами понимания являются удобочитаемость и ремонтопригодность. Многие люди находят их очень удобочитаемыми, и даже разработчики, которые никогда раньше не видели их, обычно могут правильно догадаться, что это означает.
# Snippet 1
squares = [n ** 2 for n in range(5)]
# Snippet 2
squares = []
for n in range(5):
squares.append(n ** 2)
Оба фрагмента кода произведут squares
быть равным [0, 1, 4, 9, 16]
.
Обратите внимание, что в первом фрагменте кода указывается, какой список вам нужен, а во втором - указывается, как его создать. Вот почему понимание является декларативным и высокоуровневым.
Синтаксис
[EXPRESSION for VARIABLE in SEQUENCE]
EXPRESSION
- любое выражение Python, но обычно в нем есть переменная. Эта переменная указана вVARIABLE
поле. SEQUENCE
определяет источник значений, через которые проходит переменная.
Учитывая фрагмент 1, [n ** 2 for n in range(5)]
:
EXPRESSION
являетсяn ** 2
VARIABLE
являетсяn
SEQUENCE
являетсяrange(5)
Обратите внимание: если вы проверите тип squares
вы поймете, что понимание списка - это просто обычный список:
>>> type(squares)
<class 'list'>
Подробнее о EXPRESSION
Выражение может быть любым, что сводится к значению:
- Арифметические выражения, такие как
n ** 2 + 3 * n + 1
- Вызов функции типа
f(n)
с помощьюn
как переменная - Операция среза вроде
s[::-1]
- Вызов методов
bar.foo()
- ...
Некоторые примеры:
>>> [2 * x + 3 for x in range(5)]
[3, 5, 7, 9, 11]
>>> [abs(num) for num in range(-5, 5)]
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]
>>> animals = ['dog', 'cat', 'lion', 'tiger']
>>> [animal.upper() for animal in animals]
['DOG', 'CAT', 'LION', 'TIGER']
Фильтрация:
Порядок элементов в окончательном списке определяется порядком SEQUENCE
. Однако вы можете отфильтровать элементы, добавивif
пункт:
[EXPRESSION for VARIABLE in SEQUENCE if CONDITION]
CONDITION
это выражение, которое оценивается как True
или False
. Технически состояние не должно зависеть отVARIABLE
, но обычно он его использует.
Примеры:
>>> [n ** 2 for n in range(5) if n % 2 == 0]
[0, 4, 16]
>>> animals = ['dog', 'cat', 'lion', 'tiger']
>>> [animal for animal in animals if len(animal) == 3]
['dog', 'cat']
Также помните, что Python позволяет вам писать другие виды понимания, кроме списков:
- понимание словаря
- установить понимание