Не только об операторе моржа, но и о многих связанных с ним понятиях

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

Без лишних слов, приступим.

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

В Python или языках программирования в целом есть два тесно связанных понятия: выражения и утверждения. Давайте посмотрим на две простые строки кода, как показано ниже.

>>> 5 + 3    #A
8
>>> a = 5 + 3    #B

#A - это выражение, потому что оно дает значение - целое число. Напротив, #B - это оператор, а точнее, оператор присваивания. В более общем смысле выражения - это код, который оценивает значение или объект в Python (все значения - это объекты в Python). Например, когда вы вызываете функцию (например, abs(-5)), это выражение.

Утверждения - это код, не имеющий никакого значения. Вместо этого они выполняют действие или что-то, проще говоря. Например, оператор присваивания создает переменную. Оператор with устанавливает диспетчер контекста. Операторы if…else… создают логические ответвления.

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

Вы можете задаться вопросом, почему мы здесь заботимся об этом различии. Давайте двигаться дальше.

Расширенное присвоение - это заявление

Самая распространенная форма расширенного задания, с которой многие знакомы, - это +=. Следующий код показывает вам пример.

>>> num = 5
>>> num += 4

Простой способ узнать, является ли конкретная строка кода выражением, - просто отправить ее встроенной функции print, которая должна принимать любой объект. Когда вы отправляете инструкцию на печать, она вызывает SyntaxError. Давай попробуем:

>>> print(num += 4)
  File "<input>", line 1
    print(num += 4)
              ^
SyntaxError: invalid syntax

В самом деле, мы не можем использовать расширенное присваивание в функции print. В качестве альтернативы, чтобы определить, является ли некоторый код выражением, существует другая встроенная функция eval, которая оценивает выражение. Когда код не может быть оценен, например оператор, возникает ошибка, как показано ниже.

>>> eval("num += 4")
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<string>", line 1
    num += 4
        ^
SyntaxError: invalid syntax

Фактически, x += y - это сокращение от оператора x = x + y, который является назначением по замыслу. Поскольку полученная переменная имеет то же имя (то есть x), что и имя, используемое для приращения (y), она также известна как операция сложения на месте.

То же назначение на месте также применяется к другим расширенным назначениям, таким как -=, *= и /=.

Расширенное выражение присваивания - это выражение

В отличие от этих расширенных присваиваний, которые являются операторами, новое выражение расширенного присваивания является выражением. Начнем с простого примера.

>>> (another_num := 15)
15

Как показано выше, поскольку мы используем интерактивную консоль Python, если строка кода оценивает любой объект (кроме None, который игнорируется консолью для вывода), результат оценки отображается автоматически. Таким образом, приведенное выше расширенное выражение присваивания действительно является выражением. Обратите внимание, что пара круглых скобок используется здесь по синтаксическим причинам, потому что такое расширенное выражение присваивания, используемое само по себе, не имеет практического применения и поэтому не рекомендуется.

Мы можем доказать природу выражения расширенного присваивания, являющегося выражением с функциями print и eval, как показано ниже.

>>> print(another_num := 15)
15
>>> eval("(another_num := 15)")
15

Каков реальный вариант использования?

Выражение расширенного присваивания также известно как встроенное расширенное присваивание. Как указано в строке, это встроенная операция, так что мы определяем переменную там, где она только что вычислена - по сути, мы объединяем оценку и присвоение в один шаг.

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

def withdraw_money(account_number):
    if account := locate_account(account_number):
        take_money_from(account)
    else:
        found_no_account()

Как показано выше, функция locate_account возвращает учетную запись, если она была найдена с использованием account_number. В случае обнаружения созданная переменная account из выражения присваивания далее используется в теле предложения if. В противном случае, используя порядковый номер, вот возможное решение:

def withdraw_money(account_number):
    account = locate_account(account_number)
    if account:
        take_money_from(account)
    else:
        found_no_account()

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

def withdraw_money(account_number):
    if account := locate_account_in_saving(account_number):
        take_money_from_saving(account)
    elif account := locate_account_in_checking(account_number):
        take_money_from_checking(account)
    elif account := locate_account_in_retirement(account_number):
        take_money_from_retirement(account)
    else:
        pass

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

Выводы

В этой статье мы рассмотрели технику расширенного выражения присваивания, которая добавлена ​​в Python 3.8. Мы не только изучили саму технику, но и рассмотрели ключевые концепции, связанные с этой техникой. Я надеюсь, что вы лучше понимаете эту технику и варианты ее правильного использования.

Спасибо, что прочитали эту статью. Оставайтесь на связи, подписавшись на мою рассылку новостей. Еще не являетесь участником Medium? Поддержите мое письмо, используя мою членскую ссылку (бесплатно для вас, но часть ваших членских взносов перераспределяется мне Medium в качестве стимула).