суббота, 12 декабря 2015 г.

Генераторы списков, словарей и множеств

Генератор списков


Генератор списков - это уже известные нам конструкции в новой "обертке". Внешне, они построены, в простейшей своей форме, на цикле "for" и призваны решать тотже спектр задач, что и классический цикл "for". Впринципе, нет такой задачи, которую можно выполнить через генератор списков и нельзя было бы выполнить через классический цикл "for", но сама структура генератора является краткой, лаконичной и гибкой в использовании. Держите в памяти то, что базой для генератора, служит цикл "for" и все, что мы выполняем при помощи этого цикла, мы можем выполнять и в генераторе. Рассмотрим простейший синтаксис генератора списка:

>>> lst = [1,2,3,4,5]
>>> lst
[1, 2, 3, 4, 5]
>>> [i*2 for i in lst]
[2, 4, 6, 8, 10]
>>>


Посмотрим на код более внимательно. ВНачале был создан список чисел "1,2,3,4,5", после чего следует форма записи генератора. Запись генератора списков береться в квадратные скобки. Вначале указывается действие для переменной, куда цикл помещает элементы на каждом шаге итерации, зачем следует классическая форма записи объявления цикла "for", куда передается созданный список чисел. Генератор списка возвращает список чисел, которые были сгенерированы в процессе указанного действия внутри генератора, а именно, взять элемент на каждом шаге итерации и умножить его на 2. Врезультате чего, получаем список чисел из списка "lst", Где каждый элемент умножен на 2. При использовании классического цикла "for", эта запись выглядит следующим образом:

>>> lst = [1,2,3,4,5]
>>> lst
[1, 2, 3, 4, 5]
>>> lst2 = []
>>> for i in lst:
...     lst2.append(i*2)
...
>>> lst2
[2, 4, 6, 8, 10]
>>>


Согласитесь, запись в виде генератора списка, была куда более краткой. На первый взгляд, приемущества генератора списков очевидны далеко не сразу, но давайте смоделируем несколько ситуаций, где мы можем применить генератор списка, который позволит сократить количество написанного кода, а главное, позволит сократить код не в ущерб производительности и сопровождению. Кпримеру, у нас есть файл, с которого нам необходимо достать список строк. Файл находится в кодировке "utf-8" и в процессе доставания, нам необходимо декодировать строки в юникод, параллельно убрав эскейп последовательность "\n", в конце каждой строки.

>>> text = [s.decode('utf-8')[:-1] for s in open(ur'd:\dev\price.txt')]
>>> print text[0]
Прайс на чебурашек, чубурашек и чибураторов.
>>>


Полюбуйтесь на этот код! Мы выполнили поставленную задачу в одну строку кода. Впринципе, код достаточно очевидный. Мы обратились к файлу непосредственно в цикле "for", что передало циклу объект-итератор файла. Далее цикл пробежался по строкам, и выполнил действие, которое мы указали ему вначале нашего генератора, Т.Е. Перевел каждую строку в юникод и вернул ее со срезом [:-1], что позволило удалить эскейп последовательность "\n". Собственно получившийся список, мы присвоили переменной "text", которая и содержит список обработанных строк:

>>> for i in text:
...     print i
...
Прайс на чебурашек, чубурашек и чибураторов.
Чебурашка, цвет зеленый, характер унылый, цена 100 грн;
Чебурашка, цвет красный, характер агрессивный, цена 150 грн;
Чебурашка, цвет коричневый, характер чебурашечный, цена 150 грн;
Чебурашка, цвет белый, характер настырный, цена 200 грн;
Чубурашка, цвет синий, характер мерзкий, цена 200 грн;
Чубурашка, цвет голубой, характер доставучий, цена 250 грн;
Чубурашка, цвет малиновый, характер клубничный, цена 300 грн;
Чибуратор, цвет хаки, характер нордический, цена 400 грн;
Чибуратор, цвет мутный, характер мутный, цена 400 грн;
Чибуратор, цвет шоколадный, характер мармеладный, цена 500 грн;
Чибуратор, цвет полосатый, характер сонный, цена 600 грн;
>>>


Это действительно удобно, причем, как я и говорил, не затрудняет чтение кода и положительно сказывается на общей производительности.

Генератор списков с условием "if"


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

>>> lst = [1,5,3,7,3,4,8,3,9,3,1,2,8,6,7,4,9]
>>> lst
[1, 5, 3, 7, 3, 4, 8, 3, 9, 3, 1, 2, 8, 6, 7, 4, 9]
>>> [i for i in lst if i >= 5]
[5, 7, 8, 9, 8, 6, 7, 9]
>>>


Мы расширили синтаксис генератора списков условием "if", которое имеет выражение ">= 5". Генератор вернет список только тех значений, которые будут обрабатываться на шаге итерации, при котором "if" будет получать "True". Соответственно, нам вернется список чисел, которые соответствуют утверждению ">= 5".
Мы можем еще более расширить генератор списка, воспользовавшись тернарной записью "if else":

>>> 1 if 3>2 else 0
1
>>> 1 if 3 < 2 else 0
0
>>>


Напомню, что тернарная запись "if else", возвращает результат слева, если выражение после "if", возвращает "True". В противном случае, возвращается результат справа. Теперь посмотрим, как мы можем соединить это с генератором списка, решив предыдущую задачу, но поставив условие, что, как только нам встречается число в списке меньше пяти, мы возвращаем его, но прибавив к нему +5:

>>> lst
[1, 5, 3, 7, 3, 4, 8, 3, 9, 3, 1, 2, 8, 6, 7, 4, 9]
>>> [i+5 if i < 5 else i for i in lst]
[6, 5, 8, 7, 8, 9, 8, 8, 9, 8, 6, 7, 8, 6, 7, 9, 9]
>>>


Давайте решим еще одну, менее синтетическую задачу, на основании этого синтаксиса. Возьмем нашь файлик "price.txt" и вернем в список только те строки, которые начинаются со слова "Чебурашка", а для остальных результатов, вернуть "None":

>>> text = [None if not string.startswith(u"Чебурашка".encode('utf-8')) else string.decode('utf-8')[:-1] for string in open(ur"d:\dev\price.txt")]
>>> for i in text:
...     print i
...
None
Чебурашка, цвет зеленый, характер унылый, цена 100 грн;
Чебурашка, цвет красный, характер агрессивный, цена 150 грн;
Чебурашка, цвет коричневый, характер чебурашечный, цена 150 грн;
Чебурашка, цвет белый, характер настырный, цена 200 грн;
None
None
None
None
None
None
None
>>>


(Строчный метод "startswith", возвращает "True", если строка начинается с переданного в метод префикса)

Генератор словарей


Подробно останавливаться на использовании генератора, как такового, мы не будем, рассмотрим только базовый синтаксис генератора словарей. Смысл и примеры использования, остаются такими же, как и для генератора списков:

>>> lst
[1, 2, 3, 4, 5]
>>> {i:i*2 for i in lst}
{1: 2, 2: 4, 3: 6, 4: 8, 5: 10}
>>>


Все выражение генератора мы заключаем в фигурные скобочки, а в начале, в виде действия, указываем синтаксис записи пары "ключ/значение" для словаря.

Генератор множеств


Тройку встроенных генераторов Python, заканчивает генератор множеств, суть которого остается такой же, как и у предыдущих генераторов. Его базовый синтаксис выглядит следующим образом:

>>> lst = [1,2,3,4,5]
>>> lst
[1, 2, 3, 4, 5]
>>> {i*2 for i in lst}
set([8, 2, 4, 10, 6])
>>>


Будте внимательны, генератор множеств, также, как и генератор словарей, заключается в фигурные скобочки, разница лишь в синтаксисе. Для генератора множеств, мы не указываем вид пары "ключ/значение", его синтаксис схож с генератором списков, но заключен в фигурные скобочки. Именно такой синтаксис, говорит Python, что это генератор множеств.

Комментариев нет:

Отправить комментарий