Поиск…


Вступление

Понимание списка является синтаксическим инструментом для создания списков естественным и сжатым способом, как показано в следующем коде, чтобы составить список квадратов чисел от 1 до 10: [i ** 2 for i in range(1,11)] Манекен i из существующего range списка используется для создания нового шаблона элемента. Он используется там, где цикл for необходим в менее выразительных языках.

Синтаксис

  • [i для i в диапазоне (10)] # основное понимание списка
  • [i for i in xrange (10)] # базовое понимание списка с объектом-генератором в python 2.x
  • [i для i в диапазоне (20), если i% 2 == 0] # с фильтром
  • [x + y для x в [1, 2, 3] для y в [3, 4, 5]] # вложенных циклах
  • [i, если i> 6 else 0 для i в диапазоне (10)] # тройное выражение
  • [i, если i> 4 else 0 для i в диапазоне (20), если i% 2 == 0] # с фильтром и тройным выражением
  • [[x + y для x в [1, 2, 3]] для y в [3, 4, 5]] # понимание вложенного списка

замечания

Перечисления списков были описаны в PEP 202 и представлены в Python 2.0.

Условные списки

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

[<expression> for <element> in <iterable> if <condition>]

Для каждого <element> в <iterable> ; если <condition> значение True , добавьте <expression> (обычно функцию <element> ) в возвращаемый список.


Например, это можно использовать для извлечения только четных чисел из последовательности целых чисел:

[x for x in range(10) if x % 2 == 0]
# Out: [0, 2, 4, 6, 8]

Демо-версия

Вышеприведенный код эквивалентен:

even_numbers = [] 
for x in range(10):
    if x % 2 == 0:
        even_numbers.append(x)

print(even_numbers)
# Out: [0, 2, 4, 6, 8]

Кроме того, усвоение условного списка формы [e for x in y if c] (где e и c - выражения в терминах x ) эквивалентно list(filter(lambda x: c, map(lambda x: e, y))) .

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


Обратите внимание, что это сильно отличается от ... if ... else ... условного выражения (иногда называемого тройным выражением ), которое вы можете использовать для части <expression> list>. Рассмотрим следующий пример:

[x if x % 2 == 0 else None for x in range(10)]
# Out: [0, None, 2, None, 4, None, 6, None, 8, None]

Демо-версия

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

<value-if-condition-is-true> if <condition> else <value-if-condition-is-false>

Это становится более очевидным, если вы объедините его с другими операторами:

[2 * (x if x % 2 == 0 else -1) + 1 for x in range(10)]
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Демо-версия

Если вы используете Python 2.7, xrange может быть лучше, чем range по нескольким причинам, как описано в документации xrange .

[2 * (x if x % 2 == 0 else -1) + 1 for x in xrange(10)]
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Вышеприведенный код эквивалентен:

numbers = []
for x in range(10):
    if x % 2 == 0:
        temp = x
    else:
        temp = -1
    numbers.append(2 * temp + 1)
print(numbers)
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Можно комбинировать тройные выражения и if условия. Термальный оператор работает с отфильтрованным результатом:

[x if x > 2 else '*' for x in range(10) if x % 2 == 0]
# Out: ['*', '*', 4, 6, 8]

То же самое не могло быть достигнуто только тройным оператором:

[x if (x > 2 and x % 2 == 0) else '*' for x in range(10)]
# Out:['*', '*', '*', '*', 4, '*', 6, '*', 8, '*']

См. Также: Фильтры , которые часто предоставляют достаточную альтернативу условным спискам.

Список рекомендаций с вложенными циклами

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

[ expression for target1 in iterable1 [if condition1]
             for target2 in iterable2 [if condition2]...
             for targetN in iterableN [if conditionN] ]

Например, следующий код выравнивает список списков, используя несколько for операторов:

data = [[1, 2], [3, 4], [5, 6]]
output = []
for each_list in data:
    for element in each_list:
        output.append(element)
print(output)
# Out: [1, 2, 3, 4, 5, 6]

может быть эквивалентно записано как понимание списка с несколькими for конструкций:

data = [[1, 2], [3, 4], [5, 6]]
output = [element for each_list in data for element in each_list]
print(output)
# Out: [1, 2, 3, 4, 5, 6]

Демо-версия

Как в развернутой форме, так и в понимании списка, первый цикл (первый для оператора) является первым.


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

In [1]: data = [[1,2],[3,4],[5,6]]
In [2]: def f():
   ...:     output=[]
   ...:     for each_list in data:
   ...:         for element in each_list:
   ...:             output.append(element)
   ...:     return output
In [3]: timeit f()
1000000 loops, best of 3: 1.37 µs per loop
In [4]: timeit [inner for outer in data for inner in outer]
1000000 loops, best of 3: 632 ns per loop

Накладные расходы для вызова функции выше составляют около 140 нс .


Inline, if s вложен аналогично и может возникать в любой позиции после первого for :

data = [[1], [2, 3], [4, 5]]
output = [element for each_list in data
                if len(each_list) == 2
                for element in each_list
                if element != 5]
print(output)
# Out: [2, 3, 4]

Демо-версия

Однако для удобства чтения вам следует использовать традиционные for-loops . Это особенно верно, когда вложенность более двух уровней глубокая, и / или логика понимания слишком сложна. множественное понимание списка вложенных циклов может быть подвержено ошибкам или дает неожиданный результат.

Фильтр рефакторинга и отображение списка для составления списка

Функции filter или map часто следует заменять списками . Гвидо Ван Россум описывает это хорошо в открытом письме в 2005 году :

filter(P, S) почти всегда записывается как [x for x in S if P(x)] , и это имеет огромное преимущество в том, что наиболее распространенные способы использования включают предикаты, которые являются сравнениями, например x==42 , и определяют лямбда для этого требует гораздо больше усилий для читателя (плюс лямбда медленнее, чем понимание списка). Тем более, что для map(F, S) которое становится [F(x) for x in S] . Конечно, во многих случаях вы могли бы использовать выражения генератора.

Следующие строки кода считаются « не pythonic » и будут вызывать ошибки во многих python-линтерах.

filter(lambda x: x % 2 == 0, range(10)) # even numbers < 10
map(lambda x: 2*x, range(10)) # multiply each number by two
reduce(lambda x,y: x+y, range(10)) # sum of all elements in list

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

# Filter:
# P(x) = x % 2 == 0
# S = range(10)
[x for x in range(10) if x % 2 == 0]

# Map
# F(x) = 2*x
# S = range(10)
[2*x for x in range(10)]

Читаемость становится еще более очевидной при работе с функциями цепочки. В тех случаях, когда из-за удобочитаемости результаты одной карты или функции фильтра должны быть переданы в результате к следующему; с простыми случаями, их можно заменить на единое понимание списка. Кроме того, из понимания списка мы можем легко понять, каков результат нашего процесса, где есть большая познавательная нагрузка при рассуждении о цепочке процесса Map & Filter.

# Map & Filter
filtered = filter(lambda x: x % 2 == 0, range(10))
results = map(lambda x: 2*x, filtered)

# List comprehension
results = [2*x for x in range(10) if x % 2 == 0]

Рефакторинг - краткая справочная информация

  • карта

    map(F, S) == [F(x) for x in S]
    
  • Фильтр

    filter(P, S) == [x for x in S if P(x)]
    

где F и P - функции, которые соответственно преобразуют входные значения и возвращают bool

Вложенные списки

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

#List Comprehension with nested loop
[x + y for x in [1, 2, 3] for y in [3, 4, 5]]
#Out: [4, 5, 6, 5, 6, 7, 6, 7, 8]

#Nested List Comprehension
[[x + y for x in [1, 2, 3]] for y in [3, 4, 5]]
#Out: [[4, 5, 6], [5, 6, 7], [6, 7, 8]]

Вложенный пример эквивалентен

l = []
for y in [3, 4, 5]:
    temp = []
    for x in [1, 2, 3]:
        temp.append(x + y)
    l.append(temp)

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

matrix = [[1,2,3],
          [4,5,6],
          [7,8,9]] 

[[row[i] for row in matrix] for i in range(len(matrix))]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Как вложенные for циклов, нет ограничений на то, как глубокие понимания могут быть вложены.

[[[i + j + k for k in 'cd'] for j in 'ab'] for i in '12']
# Out: [[['1ac', '1ad'], ['1bc', '1bd']], [['2ac', '2ad'], ['2bc', '2bd']]]

Итерация двух или более списков одновременно в понимании списка

Для повторения более двух списков одновременно в понимании списка можно использовать zip() как:

>>> list_1 = [1, 2, 3 , 4]
>>> list_2 = ['a', 'b', 'c', 'd']
>>> list_3 = ['6', '7', '8', '9']

# Two lists
>>> [(i, j) for i, j in zip(list_1, list_2)]
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]

# Three lists
>>> [(i, j, k) for i, j, k in zip(list_1, list_2, list_3)]
[(1, 'a', '6'), (2, 'b', '7'), (3, 'c', '8'), (4, 'd', '9')]

# so on ...


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow