1 added
1 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Обработка коллекций в основном состоит из сочетаний операций map и filter. Отсеять данные по условию, а затем преобразовать и собрать все в конечный список настолько частая задача, что в Python есть особый инструмент сочетающий в себе map и filter.</p>
1
<p>Обработка коллекций в основном состоит из сочетаний операций map и filter. Отсеять данные по условию, а затем преобразовать и собрать все в конечный список настолько частая задача, что в Python есть особый инструмент сочетающий в себе map и filter.</p>
2
<h2>Списочные выражения</h2>
2
<h2>Списочные выражения</h2>
3
<p>Вспомним задачу из предыдущих уроков. Возьмем словарь пользователей, отфильтруем тех, кто старше 10 лет, и получим список их имен.</p>
3
<p>Вспомним задачу из предыдущих уроков. Возьмем словарь пользователей, отфильтруем тех, кто старше 10 лет, и получим список их имен.</p>
4
<p>Попробуем решить ту же задачу другим способом:</p>
4
<p>Попробуем решить ту же задачу другим способом:</p>
5
<p>Вся обработка коллекции умещается в одну короткую строку. Распишем ее подробнее:</p>
5
<p>Вся обработка коллекции умещается в одну короткую строку. Распишем ее подробнее:</p>
6
<p>Теперь код стал похож на запись цикла. Сравните:</p>
6
<p>Теперь код стал похож на запись цикла. Сравните:</p>
7
<p>Код выглядит очень похоже, но есть два различия:</p>
7
<p>Код выглядит очень похоже, но есть два различия:</p>
8
<ul><li>В первом варианте мы создаем новый список, а во втором - изменяем заранее созданный</li>
8
<ul><li>В первом варианте мы создаем новый список, а во втором - изменяем заранее созданный</li>
9
<li>Первый вариант - это выражение, а второй - набор инструкций. Следовательно, первый вариант можно использовать как часть любых других выражений. При этом нам не пришлось объявлять вспомогательные функции, лямбды тоже не понадобились</li>
9
<li>Первый вариант - это выражение, а второй - набор инструкций. Следовательно, первый вариант можно использовать как часть любых других выражений. При этом нам не пришлось объявлять вспомогательные функции, лямбды тоже не понадобились</li>
10
</ul><p>Выражения вида [… for … in …] называются<strong>списочными выражениями</strong>, list comprehensions.</p>
10
</ul><p>Выражения вида [… for … in …] называются<strong>списочными выражениями</strong>, list comprehensions.</p>
11
<p>В общем виде списочное выражение описывается так:</p>
11
<p>В общем виде списочное выражение описывается так:</p>
12
<p>Рассмотрим этот шаблон подробнее:</p>
12
<p>Рассмотрим этот шаблон подробнее:</p>
13
<ul><li>ВЫРАЖЕНИЕ может использовать ПЕРЕМЕННУЮ и вычисляется в элемент будущего списка</li>
13
<ul><li>ВЫРАЖЕНИЕ может использовать ПЕРЕМЕННУЮ и вычисляется в элемент будущего списка</li>
14
<li>ПЕРЕМЕННАЯ - имя, с которым поочередно связываются элементы ИСТОЧНИКА</li>
14
<li>ПЕРЕМЕННАЯ - имя, с которым поочередно связываются элементы ИСТОЧНИКА</li>
15
<li>ИСТОЧНИК - любой итератор или итерируемый объект</li>
15
<li>ИСТОЧНИК - любой итератор или итерируемый объект</li>
16
<li>УСЛОВИЕ - выражение, которое использует ПЕРЕМЕННУЮ, вычисляемую на каждой итерации</li>
16
<li>УСЛОВИЕ - выражение, которое использует ПЕРЕМЕННУЮ, вычисляемую на каждой итерации</li>
17
</ul><p>Если условие оказывается ложным, то вычисление выражения для текущей итерации пропускается - в итоговый список новый элемент не добавится. Если условие вместе с ключевым словом if будет пропущено, то это будет эквивалентно условию if True.</p>
17
</ul><p>Если условие оказывается ложным, то вычисление выражения для текущей итерации пропускается - в итоговый список новый элемент не добавится. Если условие вместе с ключевым словом if будет пропущено, то это будет эквивалентно условию if True.</p>
18
<p>В общем случае переменных может быть несколько. Здесь тоже работает распаковка кортежей и списков, в том числе и вложенных.</p>
18
<p>В общем случае переменных может быть несколько. Здесь тоже работает распаковка кортежей и списков, в том числе и вложенных.</p>
19
<p>Вот несколько примеров:</p>
19
<p>Вот несколько примеров:</p>
20
<p>Также в списочное выражение можно добавить ветку else с помощью тернарного .. if .. else ... Общий синтаксис не меняется, только в место выражения подставляется тернарник:</p>
20
<p>Также в списочное выражение можно добавить ветку else с помощью тернарного .. if .. else ... Общий синтаксис не меняется, только в место выражения подставляется тернарник:</p>
21
<h2>Когда использовать списочные выражения</h2>
21
<h2>Когда использовать списочные выражения</h2>
22
<p>Выше мы увидели, что списочные выражения не отменяют все встроенные функции для работы с итераторами. Одно с другим отлично сочетается.</p>
22
<p>Выше мы увидели, что списочные выражения не отменяют все встроенные функции для работы с итераторами. Одно с другим отлично сочетается.</p>
23
<p>С другой стороны, лучше не смешивать их с функциями map() и filter() - это как раз взаимозаменяемые сущности. Еще не стоит их смешивать с какими-либо побочными эффектами.</p>
23
<p>С другой стороны, лучше не смешивать их с функциями map() и filter() - это как раз взаимозаменяемые сущности. Еще не стоит их смешивать с какими-либо побочными эффектами.</p>
24
<p>Это касается не только кода с функциями map() и filter(), но и вообще любых конвейеров обработки. Стоит разделять код, ответственный за работу с побочными эффектами и чистую обработку. Например, ввод-вывод - это один из основных видов побочных эффектов. Он может находиться в начале конвейера или в его конце, но не в середине.</p>
24
<p>Это касается не только кода с функциями map() и filter(), но и вообще любых конвейеров обработки. Стоит разделять код, ответственный за работу с побочными эффектами и чистую обработку. Например, ввод-вывод - это один из основных видов побочных эффектов. Он может находиться в начале конвейера или в его конце, но не в середине.</p>
25
<h2>Словарные выражения</h2>
25
<h2>Словарные выражения</h2>
26
<p>Наряду с созданием списков через выражения, в Python существует подобный способ создавать множества и словари. Главное отличие, что теперь выражение заключено в фигурные {} скобки.</p>
26
<p>Наряду с созданием списков через выражения, в Python существует подобный способ создавать множества и словари. Главное отличие, что теперь выражение заключено в фигурные {} скобки.</p>
27
<p>Создание словарей выглядят очень похоже на создание множеств. Разница заключается в том, как описывается элемент словаря.</p>
27
<p>Создание словарей выглядят очень похоже на создание множеств. Разница заключается в том, как описывается элемент словаря.</p>
28
<p>Нужно сгенерировать не только значение, но и ключ. При этом ключ надо указать через двоеточие:</p>
28
<p>Нужно сгенерировать не только значение, но и ключ. При этом ключ надо указать через двоеточие:</p>
29
<p>Обратите внимание, что в этом примере ключ 'l'имеет значение 10. Посмотрим, какие значения имели char и pos во время генерации. Для простоты будем смотреть только на позиции символа 'l':</p>
29
<p>Обратите внимание, что в этом примере ключ 'l'имеет значение 10. Посмотрим, какие значения имели char и pos во время генерации. Для простоты будем смотреть только на позиции символа 'l':</p>
30
<p>Как можно заметить, 'l' встречается в исходной строке три раза - в последнем случае как раз в позиции 10. При генерации словаря используется последнее значение для каждого из ключей, будто словарь был заполнен в подобном цикле:</p>
30
<p>Как можно заметить, 'l' встречается в исходной строке три раза - в последнем случае как раз в позиции 10. При генерации словаря используется последнее значение для каждого из ключей, будто словарь был заполнен в подобном цикле:</p>
31
<p>В примере выше порядок ключей получается тот же самый - это порядок первого появления соответствующего символа в строке. Последующие перезаписи значений этот порядок не изменят. Словари в Python запоминают порядок добавления ключей, но не порядок последующих изменений значений.</p>
31
<p>В примере выше порядок ключей получается тот же самый - это порядок первого появления соответствующего символа в строке. Последующие перезаписи значений этот порядок не изменят. Словари в Python запоминают порядок добавления ключей, но не порядок последующих изменений значений.</p>
32
<h2>Генераторные выражения</h2>
32
<h2>Генераторные выражения</h2>
33
<p>Хоть списочные и словарные выражения почти всегда заменяют использование map() и filter(), у них есть один главный недостаток - они вычисляются сразу. Ранее мы говорили, что многие функции для работы с коллекциями в питоне ленивые. Так мы можем собирать конвейеры обработки и "протаскивать" данные через них по одному, без создания промежуточных коллекций.</p>
33
<p>Хоть списочные и словарные выражения почти всегда заменяют использование map() и filter(), у них есть один главный недостаток - они вычисляются сразу. Ранее мы говорили, что многие функции для работы с коллекциями в питоне ленивые. Так мы можем собирать конвейеры обработки и "протаскивать" данные через них по одному, без создания промежуточных коллекций.</p>
34
<p>Но list и dict comprehensions всегда сразу создают коллекцию, что может быть непрактично при работе с большими данными. Более того, зачастую последовательности не нужно вычислять целиком, в конце обработки данные соберутся в какой-то вывод.</p>
34
<p>Но list и dict comprehensions всегда сразу создают коллекцию, что может быть непрактично при работе с большими данными. Более того, зачастую последовательности не нужно вычислять целиком, в конце обработки данные соберутся в какой-то вывод.</p>
35
<p>Для решения задач выше, но в ленивом подходе, существуют<strong>генераторные выражения</strong>. Выглядят они как списочные выражения, разница только в круглых скобках вместо квадратных:</p>
35
<p>Для решения задач выше, но в ленивом подходе, существуют<strong>генераторные выражения</strong>. Выглядят они как списочные выражения, разница только в круглых скобках вместо квадратных:</p>
36
<p>Как видите, результатом вычисления второго выражения является не список, а generator object - это<strong>объект-генератор</strong>, уже знакомый нам ленивый итератор.</p>
36
<p>Как видите, результатом вычисления второго выражения является не список, а generator object - это<strong>объект-генератор</strong>, уже знакомый нам ленивый итератор.</p>
37
<p>Часто можно встретить генераторное выражение в таком месте кода, где интерпретатор может однозначно понять, где границы этого выражения. Самый частый пример - генераторное выражение в роли единственного аргумента функции:</p>
37
<p>Часто можно встретить генераторное выражение в таком месте кода, где интерпретатор может однозначно понять, где границы этого выражения. Самый частый пример - генераторное выражение в роли единственного аргумента функции:</p>
38
<p>В подобных случаях скобки вокруг самого выражения можно опустить. Такое избавление от лишних скобок часто делает код еще более лаконичным:</p>
38
<p>В подобных случаях скобки вокруг самого выражения можно опустить. Такое избавление от лишних скобок часто делает код еще более лаконичным:</p>
39
<p>Код выше можно перевести так:</p>
39
<p>Код выше можно перевести так:</p>
40
<blockquote><p>Есть ли любой икс больше ста среди иксов в диапазоне от нуля до миллиона?</p>
40
<blockquote><p>Есть ли любой икс больше ста среди иксов в диапазоне от нуля до миллиона?</p>
41
</blockquote><p>Это выражение вычислится мгновенно, а числа будут проверяться по одному за раз.</p>
41
</blockquote><p>Это выражение вычислится мгновенно, а числа будут проверяться по одному за раз.</p>
42
<p>А теперь представим, что мы использовали any([… for …]). В таком случае Python тоже искал бы первое значение True в списке, но предварительно построил бы в памяти список в миллион элементов.</p>
42
<p>А теперь представим, что мы использовали any([… for …]). В таком случае Python тоже искал бы первое значение True в списке, но предварительно построил бы в памяти список в миллион элементов.</p>
43
<p>Старайтесь применять генераторные выражения везде, где это возможно. Использовать объекты-генераторы могут практически любые функции, которые работают с последовательностями в том или ином виде. Даже при вызове функции для пачки аргументов лучше использовать генераторное выражение:</p>
43
<p>Старайтесь применять генераторные выражения везде, где это возможно. Использовать объекты-генераторы могут практически любые функции, которые работают с последовательностями в том или ином виде. Даже при вызове функции для пачки аргументов лучше использовать генераторное выражение:</p>
44
-
<p>И уж тем более стоит использовать генераторные выражения посреди выражений с list, set и dict. Генер��торные выражения регулярно используются вместе с sum, any, all, а также среди конвейеров на основе map() или filter().</p>
44
+
<p>И уж тем более стоит использовать генераторные выражения посреди выражений с list, set и dict. Генераторные выражения регулярно используются вместе с sum, any, all, а также среди конвейеров на основе map() или filter().</p>