http://habrahabr.ru/post/224163/
Некоторые замечательнеые возможности языка Python незаслуженно оставлены без внимания и многие программисты о них не знают. В этот раз речь пойдет о прекрасной возможности языка, делающей код яснее: генераторы словарей — однострочные выражения возвращающие словарь. Но начнем с компактных генераторов списков и задачи удаления неуникальных элементов коллекций.
Будет интересно в основном новичкам в Python.
Генераторы списков
Самый простой способ создать список — использовать однострочное выражение — генератор списка. Они довольно часто применяется, и я встречал их во многих примерах и в коде многих библиотек.
Предположим, что у нас есть функция возвращающая какой-то список. Хороший пример — функция
range(start, end), которая возвращает числа между
start и
end. Начиная с версии Python 3.0 она реализована как
генератор и возвращает не сразу полный список, а выдает число за числом по мере необходимости. В Python 2.* для этого использовалась функция
xrange(). Получение списка чисел от 1 до 10 при помощи этой функции могло бы выглядеть так:
numbers = []
for i in range(1, 11):
numbers.append(i)
Если нам нужны только четные номера, мы могли бы реализовать это следующим образом:
numbers = []
for i in range(1, 11):
if i % 2 == 0:
numbers.append(i)
Генераторы словарей делают код намного проще. Так выглядит выражение возвращающее список в общем виде:
[ expression for item in list if conditional ]
Используя его, первый пример можно переписать так:
numbers = [i for i in range(1, 11)]
а второе так:
numbers = [i for i in range(1, 11) if i % 2 == 0]
Конечно такой синтаксис на первый взгляд может показаться странным, но когда к нему привыкнешь — код станет проще и понятнее.
Удаление дубликатов
Другая часто встречающаяся задача при работе с коллекциями — удаление одинаковых элементов. Ее можно решить множеством методов.
Допустим мы работаем с таким списком:
numbers = [i for i in range(1,11)] + [i for i in range(1,6)]
Самый сложный способ удалить дубликаты, который мне встречался, выглядит так:
unique_numbers = []
for n in numbers:
if n not in unique_numbers:
unique_numbers.append(n)
Конечно и это работает, но есть решения попроще. Вы можете использовать стандартный тип множество(set). Множества не могу содержать одинаковые элементы по определению, таким образом если конвертировать список во множество — дубликаты удалятся. Но мы получим множество а не список, поэтому если мы хотим именно список уникальных значений — нужно сконвертировать еще раз:
unique_numbers = list(set(numbers))
Удаление одинаковых объектов
Совсем другая ситуация с объектами или словарями. Например у нас есть список словарей, в которых одно из значений используется в качестве идентификатора:
data = [
{'id': 10, 'data': '...'},
{'id': 11, 'data': '...'},
{'id': 12, 'data': '...'},
{'id': 10, 'data': '...'},
{'id': 11, 'data': '...'},
]
Удаление повторов может может быть реализовано большим или меньшим количеством кода. Конечно, чем меньше — тем лучше! Длинный вариант может выглядеть, например, так:
unique_data = []
for d in data:
data_exists = False
for ud in unique_data:
if ud['id'] == d['id']:
data_exists = True
break
if not data_exists:
unique_data.append(d)
Можно получить то же результат, используя возможность, о которой я узнал пару дней назад: генераторы словарей. Они имеют похожий на генераторы списков синтаксис, но возвращают словарь:
{ key:value for item in list if conditional }
Если переписать код из примера выше с использование этой фичи, останется всего одна сточка:
{ d['id']:d for d in data }.values()
В этой строчке кода создается словарь, ключами которого являются поля, которые мы приняли за уникальный идентификатор, затем с помощью метода values() получаем все значения из созданного словаря. Т.к. словарь может содержать не больше одной записи для каждого ключа — полученный в итоге список не содержит дубликатов, что нам и требовалось.
Данная возможность была добавлена в Python 3.0 и быкпортирована в Python 2.7, В более ранних версиях для решения подобной задачи можно использовать конструкцию такого вида:
dict((key, value) for item in list if condition)
Генерируется список кортежей (пар) и передается их конструктору dict(), который берет первый элемент кортежа как ключь, а второй как значение. При таком подходе решение всё той же задачи будет выглядеть так:
dict((d['id'], d) for d in data).values()