Сообщить об ошибке.

Присваивание значений в выражении

Выражения присваивания введено в Python 3.8 и означает способ присваивания значения переменной в выражении.

name := expr

Оператор := неофициально называют как "оператор моржа", в связи его схожестью с изображением морды моржа (два глаза и усы, смотрящие вниз).

Рекомендуется вокруг := всегда ставить пробелы, аналогично рекомендации PEP 8 для = при использовании для присваивания

Применение выражения присваивания:

# Обрабатывать подходящее регулярное выражение
if (match := pattern.search(data)) is not None:
    # Сделать что-нибудь с `match`

# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
   # Сделать что-нибудь с 'chunk'

# Повторно использовать значение, которое дорого вычислять
[y := f(x), y**2, y**3]

# Совместное использование подвыражения при фильтрации последовательностей
filtered_data = [y for x in data if (y := f(x)) is not None]

Оператор := может использоваться непосредственно в позиционном аргументе функции, но недействителен непосредственно в аргументе ключевого слова. Некоторые примеры для уточнения, что является технически действительным или недействительным:

# НЕДОПУСТИМО
x := 0
# Допустимая альтернатива
(x := 0)

# НЕДОПУСТИМО
x = y := 0
# Допустимая альтернатива
x = (y := 0)

# Допустимо
len(lines := f.readlines())

# Допустимо
foo(x := 3, cat='vector')

# НЕДОПУСТИМО
foo(cat=category := 'vector')
# Допустимая альтернатива
foo(cat=(category := 'vector'))

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

if any(len(longline := line) >= 100 for line in lines):
    print("Extremely long line:", longline)

Различия между выражениями присваивания и операторами присваивания

Поскольку := является выражением, его можно использовать в тех случаях, когда операторы недопустимы, включая лямбда-функции и действиях типа [total := total + v for v in values]. И наоборот, выражения присваивания не поддерживают расширенные функции, которые можно найти в операторах присваивания:

  • Групповое присваивание не поддерживаются напрямую:

    x = y = z = 0  
    # Эквивалент 
    (z := (y := (x := 0)))
    
  • Присваивание по индексу элемента или по атрибуту не поддерживаются:

    # Нет эквивалента
    a[i] = x
    self.rest = []
    
  • Приоритет вокруг запятых отличается:

    x = 1, 2  
    print(x)
    # (1, 2)
    (x := 1, 2) 
    print(x)
    # 1
    
  • Упаковка и распаковка, как обычной, так и расширенной формы не поддерживаются:

    # Эквиваленту нужны дополнительные скобки
    loc = x, y  # Использовать  
    (loc := (x, y))
    
    info = name, phone, *rest  
    # Использовать 
    (info := (name, phone, *rest))
    
    # Нет эквивалента
    px, py, pz = position
    name, phone, email, *other_info = contact
    
  • Расширенное присваивание не поддерживается:

    total += tax  
    # Эквивалент 
    (total := total + tax)
    
  • Встроенные аннотации типа не поддерживаются:

Особый случай применения выражения присваивания.

Выражение присваивания не вводит новую область видимости. В большинстве случаев область, в которой будет привязана переменная при таком присваивании, это текущая область видимости. Если эта область видимости содержит nonlocal или global объявление для переменной, выражение присваивания учитывает это.

Выражение присваивания, встречающееся в действиях со списками, наборами или вхождениями или в выражениями генераторами, связывает переменную этого выражения еще и с областью видимости данного действия, выполняя объявление переменной в этой области, если таковая существует. Для описанного случая, ее область видимости nonlocal или global будет расширена вложенной областью действия (Лямбда считается вложенной областью действия), в которой она может быть изменена! То есть переменная выражения присваивания будет доступна для изменения как из вложенной области действия, так и из nonlocal или global.

Во-первых, это позволяет нам удобно захватывать "свидетеля" для выражения any() или контрпример для all() , например:

if any((comment := line).startswith('#') for line in lines):
    print("First comment:", comment)
else:
    print("There are no comments")

if all((nonblank := line).strip() == '' for line in lines):
    print("All lines are blank")
else:
    print("First non-blank line:", nonblank)

Во-вторых, это предоставляет компактный способ обновления изменяемого состояния переменной из вложенной области видимости, например:

# Вычислить частичные суммы в понимании списка
total = 0
partial_sums = [total := total + v for v in values]
print("Total:", total)

Однако целевая переменная выражения присваивания не может совпадать с именем for i. Переменная i являются вложенной по отношению к действиям с элементами последовательности.

Например, [i := i+1 for i in range(5)] недопустимо: часть for i устанавливает, что i является вложенной для действия с range(), но часть i := настаивает на том, что i не является внутренней для range() и расширяет ее до nonlocal или global.

# Эти выражения будут НЕ ПРАВИЛЬНЫМИ
[[(j := j) for i in range(5)] for j in range(5)]
[i := 0 for i, j in stuff]
[i+1 for i in (i := stuff)]

# Это ограничение применяется, даже если 
# выражение присваивания никогда не выполняется:
[False and (i := 0) for i, j in stuff]     # НЕ ПРАВИЛЬНО
[i for i, j in stuff if True or (j := 1)]  # НЕ ПРАВИЛЬНО