Антон Ларичев
Оператор моржа := в Python
Что такое оператор моржа
Оператор := (walrus operator, оператор моржа) был добавлен в Python 3.8 согласно PEP 572. Своё название он получил благодаря внешнему сходству с глазами и клыками моржа.
Его официальное название — оператор именованного выражения (named expression). Он позволяет присваивать значение переменной и одновременно использовать это значение как часть выражения.
До Python 3.8 присваивание и использование значения были разделены на два шага. Оператор := объединяет их в один.
Синтаксис
variable := expression
Важно понимать разницу:
# Обычное присваивание — это инструкция, не выражение
x = 10
# Оператор моржа — это выражение, которое возвращает значение
# и одновременно присваивает его переменной
(x := 10)
Ключевое свойство: := возвращает значение, поэтому его можно использовать внутри условий, циклов и генераторов.
Применение в циклах while
Самый распространённый и наглядный случай использования — цикл while, который читает данные до тех пор, пока они не закончатся.
Без оператора моржа:
line = file.readline()
while line:
process(line)
line = file.readline()
Здесь file.readline() вызывается дважды: один раз до цикла для инициализации, и ещё раз в конце каждой итерации. Это дублирование кода легко приводит к ошибкам, если нужно изменить логику чтения.
С оператором моржа:
while line := file.readline():
process(line)
Теперь readline() вызывается только в одном месте. Условие цикла само читает строку, присваивает её переменной line и проверяет, не пустая ли она.
Ещё один классический пример — чтение пользовательского ввода:
# Без оператора моржа
command = input('Введите команду: ')
while command != 'quit':
execute(command)
command = input('Введите команду: ')
# С оператором моржа
while (command := input('Введите команду: ')) != 'quit':
execute(command)
Скобки вокруг command := input(...) обязательны, потому что без них Python воспримет := неверно из-за приоритета операторов.
Применение в условных выражениях
Оператор моржа удобен, когда нужно вычислить значение, проверить его и использовать в одном блоке:
import re
text = 'Цена: 1500 рублей'
# Без оператора моржа
match = re.search(r'\d+', text)
if match:
print(f'Найдено число: {match.group()}')
# С оператором моржа
if match := re.search(r'\d+', text):
print(f'Найдено число: {match.group()}')
Во втором варианте переменная match доступна внутри блока if без предварительного объявления за его пределами.
Пример с проверкой длины строки:
def validate_username(username: str) -> str | None:
if (n := len(username)) < 3:
return f'Слишком короткое имя: {n} символов, минимум 3'
if n > 20:
return f'Слишком длинное имя: {n} символов, максимум 20'
return None
error = validate_username('ab')
if error:
print(error) # Слишком короткое имя: 2 символа, минимум 3
Без := пришлось бы вычислять len(username) дважды или заводить отдельную переменную перед условием.
Применение в генераторах списков
Генераторы списков с := позволяют избежать повторного вычисления дорогостоящей функции:
import math
numbers = [1, 4, 9, 16, -1, 25, -4]
# Без оператора моржа — sqrt вычисляется дважды
results = [
math.sqrt(n)
for n in numbers
if n >= 0 and math.sqrt(n) > 2
]
# С оператором моржа — sqrt вычисляется один раз
results = [
root
for n in numbers
if n >= 0 and (root := math.sqrt(n)) > 2
]
print(results) # [3.0, 4.0, 5.0]
Оператор := вычисляет math.sqrt(n) один раз, присваивает результат переменной root и сразу использует её в условии. Затем эта же переменная используется как результирующее значение элемента.
Другой пример — фильтрация и преобразование данных одновременно:
def parse_int(s: str) -> int | None:
try:
return int(s)
except ValueError:
return None
raw = ['1', 'abc', '3', '', '5', 'xyz']
# Получаем только валидные числа без двойного вызова parse_int
valid_numbers = [
parsed
for s in raw
if (parsed := parse_int(s)) is not None
]
print(valid_numbers) # [1, 3, 5]
Область видимости переменной
Переменная, объявленная через :=, попадает в область видимости объемлющего блока, а не ограничивается генератором или условием:
data = [1, 5, 2, 8, 3]
filtered = [y for x in data if (y := x * 2) > 6]
print(filtered) # [10, 16, 6]
print(y) # 6 — последнее присвоенное значение доступно снаружи
Это отличает := от обычных переменных внутри генераторов, которые недоступны снаружи. Будьте осторожны: если вы не ожидаете «утечки» переменной из генератора, это может привести к неожиданным результатам.
Реальный пример: парсинг конфигурации
Рассмотрим практический случай — обработка строк конфигурационного файла:
def parse_config(lines: list[str]) -> dict[str, str]:
config = {}
for line in lines:
# Пропускаем комментарии и пустые строки
line = line.strip()
if not line or line.startswith('#'):
continue
# Присваиваем результат поиска и сразу проверяем
if match := re.match(r'^(\w+)\s*=\s*(.+)$', line):
key, value = match.group(1), match.group(2)
config[key] = value
return config
lines = [
'# Настройки приложения',
'host = localhost',
'port = 8080',
'',
'debug = true',
]
print(parse_config(lines))
# {'host': 'localhost', 'port': '8080', 'debug': 'true'}
Реальный пример: поиск в связанном списке
class Node:
def __init__(self, value: int, next: 'Node | None' = None):
self.value = value
self.next = next
def find_first_greater(node: Node | None, threshold: int) -> int | None:
while node := node and node if node.value > threshold else node.next:
if node.value > threshold:
return node.value
node = node.next
return None
Впрочем, для связанных списков классический цикл while node: зачастую читабельнее. Оператор моржа — инструмент, а не цель сама по себе.
Когда не стоит использовать оператор моржа
Оператор := может сделать код короче, но не всегда — понятнее. Избегайте его в следующих случаях.
Когда переменная нужна только один раз:
# Излишнее усложнение
if (result := compute()) is not None:
print('Готово')
# Лучше так
if compute() is not None:
print('Готово')
Когда выражение становится слишком длинным:
# Трудно читать
if (data := fetch_data()) and (processed := transform(data)) and len(processed) > 10:
save(processed)
# Лучше разбить на шаги
data = fetch_data()
if data:
processed = transform(data)
if len(processed) > 10:
save(processed)
Вложенные := в одном выражении — почти всегда плохая идея:
# Избегайте такого
if (a := (b := func())) > 0:
...
Сравнение подходов: итого
| Ситуация | Без := | С := |
|---|---|---|
| Цикл с чтением данных | Дублирование вызова | Один вызов в условии |
| Проверка результата функции | Переменная до if | Переменная в условии |
| Генераторы с дорогой функцией | Двойной вызов | Один вызов через := |
| Простое условие без переиспользования | Чисто | Излишне |
Требования к версии Python
Оператор := доступен начиная с Python 3.8. Если ваш проект должен поддерживать Python 3.7 и ниже — использовать его нельзя.
Проверить версию:
python --version
# Python 3.11.4
Если вы получаете SyntaxError: invalid syntax при использовании :=, значит интерпретатор старше 3.8.
Итог
Оператор моржа := — полезный инструмент Python 3.8+, который позволяет:
- Устранить дублирование вызовов функций в условиях и циклах
- Сократить код без потери читаемости в правильном контексте
- Писать лаконичные генераторы с промежуточными вычислениями
Главное правило: применяйте := тогда, когда он делает код яснее, а не просто короче. Если выражение с := требует пауз при чтении — лучше разбить его на отдельные строки.
Чтобы глубже освоить Python и его современные возможности, приходите на курс Python для разработчиков на PurpleSchool.
Постройте личный план изучения Python до уровня Middle — бесплатно!
Python — часть карты развития Backend
100+ шагов развития
30 бесплатных лекций
300 бонусных рублей на счет
Все гайды по Python
Лучшие курсы по теме

Основы Python
Антон Ларичев
Nest.js с нуля
Антон Ларичев