Python Language
Список рекомендаций
Поиск…
Вступление
[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 ...