Моржовый оператор в Python
Приглашаем также всех желающих на двухдневный онлайн-интенсив «Разработка десктоп-приложения с помощью библиотеки Tkinter». На интенсиве получим начальные навыки бэкенд-разработки на Python, а также начнем разработку десктоп-приложения с помощью библиотеки Tkinter.

Моржовый (walrus) оператор, появившийся в Python 3.8, дает возможность решить сразу две задачи: присвоить значение переменной и вернуть это значение, поэтому порой можно написать код короче и сделать его более читаемым, и он может быть даже более эффективным с точки зрения вычислений.
Давайте посмотрим на моржовый оператор и приведем примеры того, где он может быть полезен.
Простой оператор присваивания
Все мы знаем, как присвоить значение переменной. Делается это с помощью простого оператора присваивания:
И если мы хотим вывести значение этой переменной с помощью функции print , то передать переменную num мы можем следующим образом:
Моржовый оператор
Добавленный в Python 3.8 моржовый оператор (:=), формально известен как оператор присваивания выражения. Он дает возможность присвоить переменные в выражении, включая переменные, которых еще не существует. Как было сказано выше, с помощью простого оператора присваивания (=) мы назначили num равным 15 в контексте отдельного оператора.
Выражение вычисляется как значение. А инструкция выполняет какое-то действие.
Другими словами, моржовый оператор позволяет нам как присваивать значение переменной, так и возвращать это значение в одном и том же выражении. А называется он так, потому что символ (:=) похож на глаза и бивни моржа, лежащего на боку.

Expr вычисляется, а затем присваивается имени переменной. Это значение и будет возвращено. Давайте рассмотрим некоторые примеры использования этого оператора.
Простой пример
Лучший способ понять, как работает моржовый оператор – этот простой пример. Как и выше, мы хотим присвоить 15 переменной num , а затем вывести значение num . Мы можем выполнить обе эти задачи в одной строке кода, используя новый оператор следующим образом:
Значение 15 присваивается num . Затем возвращается то же значение, которое становится аргументом для функции print . Таким образом, выводится 15.
Если мы попытаемся сделать то же самое с помощью обычного оператора присваивания, то получим ошибку типа, поскольку num = 15 ничего не возвращает.
Другой пример
Допустим, мы хотим продолжить запрашивать какие-то данные у пользователя. Если пользователь ничего не вводит, мы можем перестать запрашивать дополнительный ввод. Сделать это с помощью цикла while можно следующим образом:

Мы просим пользователя ввести что-нибудь и присваиваем вводу значение. Затем создаем цикл while , который выполняется до тех пор, пока введенное значение не пустая строка. Мы выводим «Nice!», если пользователь успешно ввел что-то. Зачем мы запрашиваем у пользователя следующий ввод, присваиваем ему значение и перезапускаем процесс.
Давайте попробуем сделать это с помощью моржового оператора:

Мы запрашиваем у пользователя входные данные и присваиваем их с помощью моржового оператора. Значение это позже возвращается и сравнивается с пустой строкой. Если в результате сравнения приходит True (то есть не равно пустой строке), код в цикле while выполняется и выводится «Nice!». Если приходит False , то дальнейший код не выполняется.
Пример со списочным выражением
Еще одним примером того, как можно улучшить читаемость кода и его вычислительную эффективность, служит списочное выражение, которое фильтрует значения.
Допустим, у нас есть список чисел num_list , и мы хотим добавить в список куб числа только в случае, если это значение меньше 20. Сделать это можно следующим образом:

Обратите внимание, что нам нужно вызывать функцию cube дважды.
Моржовый оператор позволит нам вызвать функцию cube всего один раз в нашем списочном выражении, как показано ниже:

Значение cube(x) присваивается y , затем возвращается и сравнивается с 20. Значение y будет добавлено в список только в том случае, если оно меньше 20. Обратите внимание, что функция cube() вызывается только один раз, что повышает эффективность кода. И повышение эффективности тем существеннее, чем более сложна для вычисления функция.
Надеюсь, это руководство по использованию моржового оператора в Python было вам полезно. Спасибо за прочтение!
Узнать подробнее о курсе «Python Developer. Basic«.
Регистрация на двухдневный онлайн-интенсив «Разработка десктоп-приложения с помощью библиотеки Tkinter»: день 1, день 2.
Решение модуля 11.7 из курса «Поколение Python: для начинающих»
Корректные ответы и решения урока под курс «Поколение Python: курс для начинающих». Прежний урок 11.6.
Как записывается списочное выражение?
Дополните приведенный код, используя списочное выражение так, чтобы получить новый список, содержащий строки исходного списка с удаленным первым символом.
Дополните приведенный код, используя списочное выражение, так чтобы получить новый список, содержащий длины строк исходного списка.
Дополните приведенный код списочным выражением, чтобы получить новый список, содержащий только слова длиной не менее пяти символов (включительно).
Дополните приведенный код, используя списочное выражение, так чтобы получить список всех чисел палиндромов от 100100 до 10001000.
Списочное выражение 1
На вход программе подается натуральное число nn. Напишите программу, использующую списочное выражение, которая создает список содержащий квадраты чисел от 11 до nn, а затем выводит его элементы построчно, то есть каждый на отдельной строке.Формат входных данных
На вход программе подается натуральное число.Формат выходных данных
Программа должна вывести текст в соответствии с условием задачи.Примечание. Для вывода элементов списка используйте цикл for.
Списочное выражение 2
На вход программе подается строка текста, содержащая целые числа. Напишите программу, использующую списочное выражение, которая выведет кубы указанных чисел также на одной строке.Формат входных данных
На вход программе подается строка текста, содержащая целые числа, разделенные символом пробела.Формат выходных данных
Программа должна вывести текст в соответствии с условием задачи.Примечание 1. Для вывода элементов списка используйте цикл for.
Примечание 2. Используйте метод split().
В одну строку 1
На вход программе подается строка текста, содержащая слова. Напишите программу, которая выводит слова введенной строки в столбик.Формат входных данных
На вход программе подается строка текста, содержащая слова, разделенные символом пробела.Формат выходных данных
Программа должна вывести текст в соответствии с условием задачи.Примечание. Программу можно написать в одну строку кода.
В одну строку 2
На вход программе подается строка текста. Напишите программу, использующую списочное выражение, которая выводит все цифровые символы данной строки.Формат входных данных
На вход программе подается строка текста.Формат выходных данных
Программа должна вывести текст в соответствии с условием задачи.Примечание. Программу можно написать в одну строку кода.
В одну строку 3
На вход программе подается строка текста, содержащая целые числа. Напишите программу, использующую списочное выражение, которая выведет квадраты четных чисел, которые не оканчиваются на цифру 44.Формат входных данных
На вход программе подается строка текста, содержащая целые числа, разделенные символом пробела.Формат выходных данных
Программа должна вывести текст в соответствии с условием задачи.Примечание. Программу можно написать в одну строку кода.
Функциональное программирование на Питоне
Функциональное программирование является одной из парадигм, поддерживаемых языком программирования Python. Основными предпосылками для полноценного функционального программирования в Python являются: функции высших порядков, развитые средства обработки списков, рекурсия, возможность организации ленивых вычислений. Элементы функционального программирования в Питоне могут быть полезны любому программисту, так как позволяют гармонично сочетать выразительную мощность этого подхода с другими подходами.
Содержание
Возможности
Определение и использование функции
Функция в Питоне может быть определена с помощью оператора def или лямбда-выражением. Следующие операторы эквивалентны:
В определении функции фигурируют формальные аргументы. Некоторые из них могут иметь значения по умолчанию. Все аргументы со значениями по умолчанию следуют после аргументов без значений по умолчанию.
При вызове функции задаются фактические аргументы. Например:
В начале идут позиционные аргументы. Они сопоставляются с именами формальных аргументов по порядку. Затем следуют именованные аргументы. Они сопоставляются по именам и могут быть заданы в вызове функции в любом порядке. Разумеется, все аргументы без значений по умолчанию должны быть заданы. Повторы в именах аргументов недопустимы.
Функция всегда возвращает только одно значение (или None , если значение не задано в операторе return или этот оператор не встречен по достижении конца определения функции). Однако, это незначительное ограничение, так как возвращаемым значением может быть кортеж.
Определив функцию с помощью лямбда-выражения, можно тут же ее использовать:
Лямбда-выражения удобны для определения не очень сложных функций, которые передаются затем другим функциям.
Функции в Python являются объектами первого класса, то есть, они могут употребляться в программе наравне с объектами других типов данных.
Списочные выражения
Списочные выражения (list comprehension, иногда используется термин «списковые включения») — наиболее выразительное из функциональных средств Питона. Например, для вычисления списка квадратов натуральных чисел, меньших 10, можно использовать выражение:
Встроенные функции высших порядков
В Питоне есть функции, одним из аргументом которых являются другие функции: map() , filter() , reduce() , apply() .
Функция map() позволяет обрабатывать одну или несколько последовательностей с помощью заданной функции:
Аналогичного (только при одинаковой длине списков) результата можно добиться с помощью списочных выражений:
filter()
Функция filter() позволяет фильтровать значения последовательности. В результирующем списке только те значения, для которых значение функции для элемента истинно:
Тоже самое с помощью списковых выражений:
reduce()
Для организации цепочечных вычислений в списке можно использовать функцию reduce() . Например, произведение элементов списка может быть вычислено так:
Вычисления происходят в следующем порядке:
Цепочка вызовов связывается с помощью промежуточного результата ( res ). Если список пустой, просто используется третий параметр (в случае произведения нуля множителей это 1):
Разумеется, промежуточный результат необязательно число. Это может быть любой другой тип данных, в том числе и список. Следующий пример показывает реверс списка:
Для наиболее распространенных операций в Питоне есть встроенные функции:
В Python 3 встроенной функции reduce() нет, но её можно найти в модуле functools .
apply()
Функция для применения другой функции к позиционным и именованным аргументам, заданным списком и словарем соответственно:
В последних версиях Питона вместо функции apply() можно применить специальный синтаксис (в продолжение примера выше):
Замыкания
Функции, определяемые внутри других функций, представляют собой полноценные замыкания (англ. closures ):
Итераторы
Другие средства функционального программирования доступны из стандартной библиотеки (например, модуль itertools ) и других библиотек.
Следующий пример иллюстрирует применение перечисляющего и сортирующего итераторов (итератор не может быть напечатан оператором print , поэтому оставшиеся в нем значения были помещены в список):
Следующий пример иллюстрирует использование модуля itertools :
В следующем примере иллюстрируется функция groupby (группировать по), с помощью которой порождается список пар значение ключа и соответствующий ключу итератор (в этот итератор собраны все значения исходного списка с одинаковым значением ключа). В примере ключом является True или False в зависимости от положительности значения. (Для целей вывода каждый итератор превращается в список).
В модуле itertools есть и другие функции для работы с итераторами, позволяющие кратко (в функциональном стиле) и с вычислительной точки зрения — эффективно — выразить требуемые процессы обработки списков.
Модуль functools
В Python 2.5 появился модуль functools и в частности возможность частичного применения функций:
(Частичное применение функций также можно реализовать с помощью замыканий или функторов )
Ленивые вычисления
Ленивые вычисления можно организовать в Python несколькими способами, используя различные механизмы:
- простейшие логические операции or и and не вычисляют второй операнд, если результат определяется первым операндом
- лямбда-выражения
- определенные пользователем классы с ленивой логикой вычислений [1] или функторы
- Генераторы и генераторные выражения
- (Python 2.5) if-выражение имеет «ленивую» семантику (вычисляется только тот операнд, который нужен)
Пример, который иллюстрирует работу if-выражения. С помощью оператора print можно проследить, какие функции реально вызывались:
Некоторые примеры из книги рецептов:
- Ленивая сортировка
- Ленивый обход графа
- Ленивое вычисление свойств
- Карринг
Функторы
Функторами называют объекты, синтаксически подобные функциям, то есть поддерживающие операцию вызова. Для определения функтора нужно перегрузить оператор () с помощью метода __call__ . В Python функторы полностью аналогичны функциям, за исключением специальных атрибутов ( func_code и некоторых других). Например, функторы можно передавать в качестве функций обратного вызова (callback) в С-код. Функторы позволяют заменить некоторые приёмы, связанные с использованием замыкания, статических переменных и т. п.
Ниже представлено замыкание и эквивалентный ему функтор:
Следует отметить, что код, использующий замыкание, будет исполнятся быстрее, чем код с функтором. Это связанно с необходимостью получения атрибута val у переменной self (т.е. функтор проделывает на одну Python операцию больше). Также функторы нельзя использовать для создания декораторов с параметрами. С другой стороны, функторам доступны все возможности ООП в Python, что делает их очень полезными для функционального программирования. Например, можно написать функтор, который будет «запоминать» исполняемые над ним операции и затем повторять их. Для этого достаточно соответствующим образом перегрузить специальные методы.
Функторы привносят в Python возможность ленивых вычислений, присущую функциональным языкам: вместо вычисления результата выражения — динамическое определение новых функций комбинированием имеющихся.
Определенный подобным образом функтор создает значительные накладные расходы, так как при каждом вызове проходит по вызовам всех вложенных lambda . Можно оптимизировать функтор, применив технику генерирования байткода во время исполнения. Соответствующий пример и тесты на скорость есть в Примерах Python программ. При использовании этой техники скорость исполнения не будет отличаться от «статического» кода (если не считать времени, требующегося на однократное конструирование результирующей функции). Вместо байтокода Python можно генерировать на выходе, например, код на языке программирования C, других языках программирования или XML-файлы.
Несмотря на накладные расходы, ленивое вычисление может дать заметный выигрыш в скорости в случаях, когда действия, оборачиваемые ленивым функтором, достаточно дороги — например, включают объёмные вычисления или доступ к диску. Предположим некоторый промежуточный результат X лениво вычисляется перед условным оператором; для него будет создана цепочка функторов. В той ветке условного оператора, где значение X не требуется по ходу вычисления, эта цепочка функторов будет просто отброшена, не приведя к дорогостоящему вычислению. В другой ветке, где X требуется для вычисления конечного результата функции, цепочка функторов произведёт его вычисление. При этом программисту не нужно отслеживать, в какой из веток алгоритма значение может не потребоваться: он может рассчитывать, что дорогостоящее вычисление X произойдёт только тогда, когда его результат не будет отброшен.