Синтаксис и выражения присваивания ":=": что и почему?

PEP 572 представляет выражения присваивания, реализованные для Python 3.8. Это кажется действительно существенной новой функцией, поскольку она позволяет использовать эту форму назначения в пределах функций понимания и лямбда-функций.

Что такое синтаксис, семантика и грамматическая спецификация выражений присваивания? (Обратите внимание, что я знаю, что люди могут просто прочитать PEP 572, но это все еще кажется полезным справочным вопросом для SO.)

Почему вводится эта новая (и, казалось бы, довольно радикальная концепция), когда аналогичная идея в PEP 379 "Добавление выражения присваивания" была ранее отклонена?

6 ответов

PEP 572 содержит много деталей, особенно по первому вопросу. Я постараюсь кратко суммировать / процитировать некоторые из наиболее важных частей PEP:

обоснование

Разрешение этой формы присваивания внутри пониманий, таких как списки и лямбда-функции, где традиционные присвоения запрещены. Это также может облегчить интерактивную отладку без необходимости рефакторинга кода.

Синтаксис и семантика

В любом контексте, где могут использоваться произвольные выражения Python, может появляться именованное выражение. Это имеет форму name := expr где expr является любым допустимым выражением Python, а name является идентификатором.

Значение такого именованного выражения совпадает со встроенным выражением, но с дополнительным побочным эффектом назначению целевому значению этого значения

Отличия от регулярных заданий

В дополнение к выражению, а не к выражению, в PEP упоминается несколько отличий: назначения выражений идут справа налево, имеют различный приоритет вокруг запятых и не поддерживают:

  • Несколько целей
x = y = z = 0  # Equivalent: (z := (y := (x := 0)))
  • Присвоения не одному имени:
# No equivalent
a[i] = x
self.rest = []
  • Повторяемая упаковка / распаковка
# Equivalent needs extra parentheses

loc = x, y  # Use (loc := (x, y))
info = name, phone, *rest  # Use (info := (name, phone, *rest))

# No equivalent

px, py, pz = position
name, phone, email, *other_info = contact
  • Встроенные аннотации типа:
# Closest equivalent is "p: Optional[int]" as a separate declaration
p: Optional[int] = None
Augmented assignment is not supported:

total += tax  # Equivalent: (total := total + tax)

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

а) Упрощение списка понимания

например:

stuff = [(lambda y: [y,x/y])(f(x)) for x in range(5)]

может стать:

stuff = [[y := f(x), x/y] for x in range(5)]

б) Получение условных значений

например (в Python 3):

command = input("> ")
while command != "quit":
    print("You entered:", command)
    command = input("> ")

может стать:

while (command := input("> ")) != "quit": print("You entered:", command)

Несколько моих любимых примеров того, как выражения присваивания могут сделать код более кратким и легким для чтения:

if заявление

До:

match = pattern.match(line)
if match:
    return match.group(1)

После:

if match := pattern.match(line):
    return match.group(1)

бесконечность while заявление

До:

while True:
    data = f.read(1024)
    if not data:
        break
    use(data)

После:

while data := f.read(1024):
    use(data)

В ПКП есть и другие хорошие примеры.

Еще несколько примеров и объяснений после официального выпуска 3.8.

Присвоение имени результату выражения - важная часть программирования, позволяющая использовать описательное имя вместо более длинного выражения и допускающее повторное использование. В настоящее время эта функция доступна только в форме оператора, что делает ее недоступной для понимания списков и других контекстов выражений.

Источник: комментарий Reddit LicensedProfessional

Обработка согласованного регулярного выражения

if (match := pattern.search(data)) is not None:
    # Do something with match

Цикл, который нельзя тривиально переписать с помощью 2-arg iter()

while chunk := file.read(8192):
   process(chunk)

Повторно используйте значение, которое сложно вычислить

[y := f(x), y**2, y**3]

Совместное использование подвыражения между предложением фильтра понимания и его выводом

filtered_data = [y for x in data if (y := f(x)) is not None]

Что такое := оператор?

Проще говоря , := — это выражение + оператор присваивания. он выполняет выражение и присваивает результат этого выражения одной переменной.

Зачем нужен оператор :=?

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

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

      [add_one(num) for num in numbers if add_one(num) > 0]

Случай 1:

      def add_one(num):
    return num + 1

numbers = [1,2,3,4,-2,45,6]


result1 = [value for num in numbers if (value := add_one(num)) > 0]
>>> result1
[2, 3, 4, 5, 46, 7]

Результат ожидаемый, и нам не нужно дважды вызывать функцию add_one, что показывает преимущество оператора :=

будьте осторожны с оператором walarus := при использовании понимания списка

приведенные ниже случаи могут помочь вам лучше понять использование оператора :=

Случай 2:

      def add_one(num):
    return num + 1

numbers = [1,2,3,4,-2,45,6]

>>> result2 = [(value := add_one(num)) for num in numbers if value > 0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
NameError: name 'value' is not defined

Случай 3: глобальная переменная имеет положительное значение

      def add_one(num):
    return num + 1

numbers = [1,2,3,4,-2,45,6]

value = 1

result3 = [(value := add_one(num)) for num in numbers if value > 0]
>>> result3
[2, 3, 4, 5, -1]

Случай 4: глобальная переменная имеет отрицательное значение

      def add_one(num):
    return num + 1

numbers = [1,2,3,4,-2,45,6]

value = -1

result4 = [(value := add_one(num)) for num in numbers if value > 0]
>>> result4
[]

Оператор Моржа можно использовать, чтобы избежать перерасчета некоторых функций. Вот пример.

В случае 1 мы выполняем complexOperation() дважды только потому, что не уверены в результате. Этого можно избежать, практикуя вариант 2.

Примечание. Мы также можем объявить переменную результата перед «циклом IF» и один раз вычислить complexOperation(). Но суть Python заключается в уменьшении количества строк.

      # WALRUS OPERATOR :
Without walrus operator :
            card_number =input("Enter card number")
            if len(card_number)==8:
            print ("Card is valid")
            else:
            print("card is invalid")

With walrus operator :
            print ("card is valid") if len (card_number :=input("Enter card number"))==8 else print("card is valid,Enter card number again")
Другие вопросы по тегам