Синтаксис и выражения присваивания ":=": что и почему?
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")