Home

apirobot

22 Jul 2017

Listcomps, dictcomps, setcomps в python

Списковые включения (listcomps) - одна из моих любимых фич в python. Благодаря им код становится короче, понятнее, быстрее, и ими не сложно научиться пользоваться. Если вы до сих пор не используете списковые включения, то клянусь, вы очень многое упускаете.

Мне кажется, что лучший способ показать, как работают списковые включения, это привести несколько примеров.

Если у вас есть список чисел, и вам нужно возвести каждое число в квадрат, то вот так это можно сделать с помощью обычного цикла for:

>>> squares = []
>>> for number in range(5):
...     squares.append(number ** 2)
>>> squares
[0, 1, 4, 9, 16]

Тоже самое можно сделать с помощью списковых включений:

>>> squares = [number ** 2 for number in range(5)]
>>> squares
[0, 1, 4, 9, 16]

Думаю, так гораздо лучше.
Кроме цикла for, в списковых включениях можно использовать условный оператор if:

>>> squares_of_even_numbers = [number ** 2 for number in range(5)
...                                        if number % 2 == 0]
>>> squares_of_even_numbers
[0, 4, 16]

Тоже самое, только с помощью обычного цикла:

>>> squares_of_even_numbers = []
>>> for number in range(5):
...     if number % 2 == 0:
...         squares_of_even_numbers.append(number ** 2)
>>> squares_of_even_numbers
[0, 4, 16]

Функции map и filter

В функциональном программировании часто используются функции map и filter. В python эти функции есть, но их с легкостью можно заменить списковыми включениями, и код после этого становится гораздо проще.

Если вы раньше не использовали функцию map, то вот что она делает (дальше надо не много напрячь извилины). Она принимает первым параметром функцию, а вторым - итерируемый объект (например список). Вернет она новый итерируемый объект, только каждый элемент этого итерируемого объекта будет результат применения функции (которую мы передали первыми параметром) к каждому элементу итерируемого объекта (который мы передали вторым параметром). Вот пример, после него все должно стать на свои места:

>>> def reverse(x):
...    return x[::-1]
>>> names = ['Denis', 'Kate', 'Ann', 'Peter']

>>> list(map(reverse, names))
['sineD', 'etaK', 'nnA', 'reteP']

Как я сказал ранее, функция map возвращает итерируемый объект, поэтому нам пришлось явно преобразовать его в список.

Теперь, как можно заменить функцию map списковыми включениями:

>>> def reverse(x):
...    return x[::-1]
>>> names = ['Denis', 'Kate', 'Ann', 'Peter']

>>> [reverse(name) for name in names]
['sineD', 'etaK', 'nnA', 'reteP']

С функцией filter все проще. Из названия понятно, что она делает. Вот пример использования функции filter, и как ее можно заменить списковыми включениями:

>>> def is_positive(number):
...     return number > 0
>>> numbers = [-5, 25, -6, 5, 2, -11]

# filter function
>>> list(filter(is_positive, numbers))
[25, 5, 2]

# listcomp
>>> [number for number in numbers if number > 0]
[25, 5, 2]

Функция filter, как и map, принимает первым параметром функцию, а вторым - итерируемый объект. Возвращает она тоже итерируемый объект. Алгоритм работы функции: она проходит по каждому элементу итерируемого объекта, и применяет к нему функцию, которую мы передали в нее первым параметром. Если функция вернет истинное значение, то она “оставляет” этот элемент, в противном случае она “выбрасывает” его.

Dictcomps и setcomps

Словари и множества можно создать таким же образом, как мы создавали списки с помощью списковых включений. Если вы уже разобрались со списковыми включениями, то вам не составит труда разобраться со словарными включениями (dictcomps) и множественными включениями (setcomps), потому что принцип один и тот же.

Пример словарного включения (dictcomp):

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

Пример множественного включения (setcomp):

>>> { n for n in range(5) }
{0, 1, 2, 3, 4}

Заключение

Используйте включения (listcomps, dictcomps, setcomps) там, где это возможно, но не создавайте слишком длинные и сложные включения, иначе ваш код станет не читабельным. В каких-то случаях лучше обойтись старым добрым циклом for. И старайтесь использовать включения вместо функций map и filter. Так код выглядит понятнее и питоничнее.

До следующего раза,
apirobot в 08:00

comments powered by Disqus