Моржовый оператор := в Python
Обновлено:
Начиная с Python 3.8 в языке появился новый оператор:
:=
Его официальное название:
assignment expression
то есть выражение присваивания.
Также его часто называют:
walrus operator
или по-русски:
моржовый оператор
Название появилось из-за внешнего вида:
:=
который напоминает морду моржа: : можно представить как глаза, а = — как клыки.
Оператор был добавлен в Python в рамках PEP 572.
1. Обычное присваивание =
До этого переменную мы создавали обычным присваиванием:
text = 'Python'
print(text)
Сначала выполняется:
text = 'Python'
и только потом переменную можно использовать.
2. = является не выражением, а отдельной инструкцией присваивания
Например, так написать нельзя:
if text = input('Введите текст: '):
print(text)
Получим:
SyntaxError
Потому что после:
if
Python ожидает выражение, результат которого можно проверить.
Обычное:
text = ...
таким выражением не является.
3. Моржовый оператор решает эту задачу
Оператор:
:=
позволяет:
- вычислить выражение;
- сохранить результат в переменную;
- сразу же вернуть это значение как результат самого выражения.
Например:
print(
length := len('Python')
)
Результат:
6
При этом переменная:
length
тоже была создана:
print(length)
Результат:
6
4. Как работает :=
В выражении:
length := len('Python')
сначала выполняется:
len('Python')
Получаем:
6
Затем:
length
начинает ссылаться на:
6
И одновременно всё выражение:
length := len('Python')
тоже даёт значение:
6
Поэтому его можно сразу передать в print().
Обычный вариант и :=
5. Без моржового оператора
length = len('Python')
print(length)
6. С моржовым оператором
print(
length := len('Python')
)
Здесь создание переменной и использование результата объединились в одно выражение.
Использование внутри if
7. Получаем пользовательский ввод
Без :=:
user_input = input(
'Введите текст: '
).strip()
if user_input:
print(
f'Вы ввели: {user_input}'
)
else:
print('Строка пустая')
С моржовым оператором:
if (
user_input := input(
'Введите текст: '
).strip()
):
print(
f'Вы ввели: {user_input}'
)
else:
print('Строка пустая')
8. Что здесь происходит
Сначала:
input(
'Введите текст: '
).strip()
возвращает строку.
Затем эта строка сохраняется:
user_input := ...
После этого полученная строка сразу используется как условие:
if ...
Если строка непустая:
True
выполняется первая ветка.
Если пользователь ничего не ввёл:
''
пустая строка преобразуется в:
False
и выполняется else.
Присваивание и дополнительная проверка
9. Проверяем чётность числа
Можно не просто создать переменную, но и сразу использовать её в более крупном выражении:
if (
number := int(
input('Введите число: ')
)
) % 2 == 0:
print(
f'{number} — чётное число'
)
else:
print(
f'{number} — нечётное число'
)
Например, пользователь вводит:
12
Сначала выполняется:
int(
input(...)
)
Получаем:
12
Дальше:
number := 12
Теперь:
number
ссылается на 12.
А условие продолжает вычисляться:
12 % 2 == 0
Получаем:
True
10. Проверка строки
Например:
if (
text := input(
'Введите строку: '
).strip().lower()
).startswith('python'):
print(
f'Подходящая строка: {text}'
)
else:
print('Строка не подходит')
Здесь результат цепочки:
input(...)
.strip()
.lower()
одновременно:
- сохраняется в
text; - используется для проверки через
startswith().
Где особенно полезен :=
Моржовый оператор удобен, когда одно и то же вычисленное значение требуется:
- проверить;
- а затем использовать повторно.
Без := приходится сначала отдельно создавать переменную.
С := иногда эти действия можно объединить.
while и моржовый оператор
11. Ввод чисел без :=
Предположим, пользователь вводит числа.
Число:
0
означает окончание ввода.
Один из вариантов:
total = 0
while True:
number = int(
input(
'Введите число, '
'0 — закончить: '
)
)
if number == 0:
break
total += number
print(total)
12. Вариант с :=
Можно написать:
total = 0
while (
number := int(
input(
'Введите число, '
'0 — закончить: '
)
)
) != 0:
total += number
print(total)
Здесь конструкция:
number := int(input(...))
получает число и сразу сохраняет его в:
number
После этого проверяется:
number != 0
Если:
True
выполняется тело цикла.
Если пользователь ввёл:
0
получаем:
0 != 0
то есть:
False
и цикл заканчивается.
13. Ещё более короткая проверка
Так как:
bool(0)
возвращает:
False
а любое другое целое число обычно:
True
можно написать:
total = 0
while number := int(
input(
'Введите число, '
'0 — закончить: '
)
):
total += number
print(total)
Но вариант:
while (
number := int(input(...))
) != 0:
явнее показывает условие завершения, поэтому для учебного и читаемого кода зачастую удобнее именно он.
while со строками
14. Ввод строк до пустой строки
words = []
while word := input(
'Введите слово: '
).strip():
words.append(word)
print(words)
Например:
Введите слово: Python
Введите слово: SQL
Введите слово: Django
Введите слово:
После пустого ввода цикл заканчивается.
Получим:
['Python', 'SQL', 'Django']
Здесь := особенно удобно: значение одновременно сохраняется и проверяется.
:= внутри list comprehension
15. Накопительная сумма
Возьмём:
numbers = [
1,
2,
3,
4,
5,
6,
7,
8,
]
Создадим:
total = 0
result = [
total := total + number
for number in numbers
]
print(result)
Результат:
[1, 3, 6, 10, 15, 21, 28, 36]
16. Как работает этот алгоритм
Изначально:
total = 0
Первый элемент:
number = 1
Выполняется:
total := total + number
То есть:
0 + 1 = 1
Теперь:
total = 1
и в новый список добавляется:
1
Следующая итерация:
total = 1
number = 2
Получаем:
1 + 2 = 3
В список добавляется:
3
Дальше:
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15
...
И получаем накопительные суммы:
[1, 3, 6, 10, 15, 21, 28, 36]
17. total остаётся доступен
После:
result = [
total := total + number
for number in numbers
]
можно проверить:
print(total)
Результат:
36
Это важная особенность assignment expression внутри comprehension: имя, которому присваивается значение через :=, связывается во внешней для comprehension области видимости.
Ещё один пример с list comprehension
18. Вычисляем длину строки только один раз
words = [
'cat',
'python',
'sql',
'javascript',
'code',
]
result = [
(
word,
length,
)
for word in words
if (
length := len(word)
) >= 5
]
print(result)
Получим:
[
('python', 6),
('javascript', 10),
]
Здесь:
length := len(word)
одновременно:
- вычисляет длину;
- сохраняет её;
- позволяет использовать её в условии;
- позволяет затем положить её в результат.
Без := пришлось бы либо вычислять:
len(word)
несколько раз, либо переписывать алгоритм обычным циклом.
Когда := действительно полезен
19. Убираем повторное вычисление
Представим:
text = 'Python backend developer'
Без :=:
words = text.split()
if len(words) >= 3:
print(words)
Здесь код и так хороший.
Но если значение нужно получить непосредственно внутри условия и потом использовать:
if (
words := text.split()
) and len(words) >= 3:
print(words)
мы можем не создавать отдельную строку присваивания.
Когда := использовать не нужно
20. Не сокращаем код любой ценой
Можно написать:
print(
number := 100
)
Но если никакой пользы от объединения действий нет:
number = 100
print(number)
обычно читается проще.
Моржовый оператор существует не для того, чтобы заменить обычный:
=
во всей программе.
Он особенно полезен тогда, когда значение нужно одновременно сохранить и использовать внутри выражения.
= и :=
21. Главное различие
Обычное присваивание:
number = 10
создаёт или переназначает переменную.
Но саму конструкцию нельзя свободно использовать там, где требуется выражение.
Моржовый оператор:
number := 10
не только выполняет присваивание, но и имеет результат:
10
Поэтому его можно использовать внутри других выражений.
Например:
if (number := 10) > 5:
print(number)
Скобки
22. Скобки часто делают код понятнее
Например:
if (
number := int(
input('Введите число: ')
)
) > 0:
print(number)
Скобки позволяют хорошо видеть:
что именно присваивается
и:
что затем проверяется
Поэтому при более сложных выражениях их использование делает код понятнее.
Нельзя путать := и именованный аргумент
Например:
print(
value=10
)
здесь:
value=10
не означает создание переменной.
Так записываются именованные аргументы функции.
Если функция не имеет такого параметра, может возникнуть:
TypeError
Это совершенно другая конструкция.
Assignment expression выглядит именно так:
value := 10
Главное по теме
Моржовый оператор:
:=
появился в:
Python 3.8
и был добавлен через:
PEP 572
Его официальное название:
assignment expression
Он позволяет одновременно:
вычислить значение
↓
сохранить его в переменную
↓
сразу использовать это значение
Например:
if (
text := input(
'Введите текст: '
).strip()
):
print(text)
Особенно удобно использовать :=:
if ...
while ...
в некоторых:
list comprehensions
и других выражениях, где вычисленный результат нужно сразу проверить или использовать.
Моржовый оператор не заменяет обычное присваивание:
=
Если обычная запись:
value = expression
понятнее, сокращать её не нужно.
:= особенно полезен тогда, когда позволяет избежать повторного вычисления или лишнего дублирования кода.
Задачи
Задача 1. Получение списка слов
Напишите функцию:
get_words_if_enough()
Функция принимает:
- строку
text; - минимальное количество слов
min_count.
Необходимо:
- разделить строку на слова с помощью
split(); - использовать моржовый оператор при получении списка слов;
- если количество слов не меньше
min_count, вернуть список слов; - иначе вернуть пустой список.
Примеры:
print(
get_words_if_enough(
'Python SQL Django',
3,
)
)
# ['Python', 'SQL', 'Django']
print(
get_words_if_enough(
'Python SQL',
3,
)
)
# []
print(
get_words_if_enough(
'Python backend developer',
2,
)
)
# ['Python', 'backend', 'developer']
Задача 2. Накопительные суммы
Напишите функцию:
get_running_totals()
Функция принимает список чисел и возвращает новый список накопительных сумм.
Необходимо использовать:
- list comprehension;
- моржовый оператор.
Например:
[1, 2, 3, 4]
должно превратиться в:
[1, 3, 6, 10]
Потому что:
1
1 + 2 = 3
1 + 2 + 3 = 6
1 + 2 + 3 + 4 = 10
Тесты:
print(
get_running_totals(
[1, 2, 3, 4]
)
)
# [1, 3, 6, 10]
print(
get_running_totals(
[10, 20, 30]
)
)
# [10, 30, 60]
print(
get_running_totals(
[5]
)
)
# [5]
Задача 3. Деление числа пополам
Напишите функцию:
get_halves()
Функция принимает положительное целое число.
Необходимо с помощью:
while
и моржового оператора постоянно выполнять целочисленное деление числа на 2.
Каждый полученный положительный результат нужно добавлять в новый список.
Когда результат станет равен 0, цикл должен закончиться.
Например:
20
↓
10
↓
5
↓
2
↓
1
↓
0
Функция должна вернуть:
[10, 5, 2, 1]
Тесты:
print(
get_halves(20)
)
# [10, 5, 2, 1]
print(
get_halves(8)
)
# [4, 2, 1]
print(
get_halves(1)
)
# []