Моржовый оператор в Python +9


AliExpress RU&CIS

Перевод подготовлен в рамках курса "Python Developer. Basic".

Приглашаем также всех желающих на двухдневный онлайн-интенсив «Разработка десктоп-приложения с помощью библиотеки Tkinter». На интенсиве получим начальные навыки бэкенд-разработки на Python, а также начнем разработку десктоп-приложения с помощью библиотеки Tkinter.


Моржовый (walrus) оператор, появившийся в Python 3.8, дает возможность решить сразу две задачи: присвоить значение переменной и вернуть это значение, поэтому порой можно написать код короче и сделать его более читаемым, и он может быть даже более эффективным с точки зрения вычислений.

Давайте посмотрим на моржовый оператор и приведем примеры того, где он может быть полезен.

Простой оператор присваивания

Все мы знаем, как присвоить значение переменной. Делается это с помощью простого оператора присваивания:

num = 15

И если мы хотим вывести значение этой переменной с помощью функции print, то передать переменную num мы можем следующим образом:

print(num)
# 15

Моржовый оператор

Добавленный в Python 3.8 моржовый оператор (:=), формально известен как оператор присваивания выражения. Он дает возможность присвоить переменные в выражении, включая переменные, которых еще не существует. Как было сказано выше, с помощью простого оператора присваивания (=) мы назначили num равным 15 в контексте отдельного оператора.

Выражение вычисляется как значение. А инструкция выполняет какое-то действие.

Другими словами, моржовый оператор позволяет нам как присваивать значение переменной, так и возвращать это значение в одном и том же выражении. А называется он так, потому что символ (:=) похож на глаза и бивни моржа, лежащего на боку.

        

Expr вычисляется, а затем присваивается имени переменной. Это значение и будет возвращено. Давайте рассмотрим некоторые примеры использования этого оператора.

Простой пример

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

print(num := 15)
# 15

Значение 15 присваивается num. Затем возвращается то же значение, которое становится аргументом для функции print. Таким образом, выводится 15.

Если мы попытаемся сделать то же самое с помощью обычного оператора присваивания, то получим ошибку типа, поскольку num = 15 ничего не возвращает.

print(num = 15)
# TypeError

Другой пример

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

Мы просим пользователя ввести что-нибудь и присваиваем вводу значение. Затем создаем цикл while, который выполняется до тех пор, пока введенное значение не пустая строка. Мы выводим «Nice!», если пользователь успешно ввел что-то. Зачем мы запрашиваем у пользователя следующий ввод, присваиваем ему значение и перезапускаем процесс.

Давайте попробуем сделать это с помощью моржового оператора:

Мы запрашиваем у пользователя входные данные и присваиваем их с помощью моржового оператора. Значение это позже возвращается и сравнивается с пустой строкой. Если в результате сравнения приходит True (то есть не равно пустой строке), код в цикле while выполняется и выводится «Nice!». Если приходит False, то дальнейший код не выполняется.

Пример со списочным выражением

Еще одним примером того, как можно улучшить читаемость кода и его вычислительную эффективность, служит списочное выражение, которое фильтрует значения.

Допустим, у нас есть список чисел num_list, и мы хотим добавить в список куб числа только в случае, если это значение меньше 20. Сделать это можно следующим образом:

Обратите внимание, что нам нужно вызывать функцию cube дважды.

Моржовый оператор позволит нам вызвать функцию cube всего один раз в нашем списочном выражении, как показано ниже:

Значение cube(x) присваивается y, затем возвращается и сравнивается с 20. Значение y будет добавлено в список только в том случае, если оно меньше 20. Обратите внимание, что функция cube() вызывается только один раз, что повышает эффективность кода. И повышение эффективности тем существеннее, чем более сложна для вычисления функция.

Надеюсь, это руководство по использованию моржового оператора в Python было вам полезно. Спасибо за прочтение!


Узнать подробнее о курсе "Python Developer. Basic".

Регистрация на двухдневный онлайн-интенсив «Разработка десктоп-приложения с помощью библиотеки Tkinter»: день 1, день 2.




Комментарии (20):

  1. besitzeruf
    /#23002692 / +7

    Лучшеб гнались за сокращением времени для понимания написанного кода! Строчку мы пишем один раз, а вот читаем гораздо чаще. И каждый раз всматриваться и вникать в каждый символ сильно утомляет… Хнык, хнык :-(

  2. mSnus
    /#23002802

    На русском лучше бы назвали по-другому. А то получается хреновый оператор...

    • mister_pibodi
      /#23003778

      Лучше бы сделали кошачий оператор, типа =^..^=

  3. baldr
    /#23002994 / +1

    Если мы попытаемся сделать тоже самое с помощью обычного оператора присваивания, то получим ошибку типа, поскольку num = 15 ничего не возвращает.

    Сюрприз: x = y = 4. Все вопросы - только к парсеру Питона. И к авторам, которым, вместо того чтобы расширить возможности оператора присваивания придумали новый недооператор.

    while (value := input("Please enter something: ") != ''):

    И, конечно, здесь та самая ошибка, которая и возникнет у большинства при использовании этого оператора. Здесь value будет равна либо True либо False. То есть ввод теряется. Нужно либо добавить скобочки, либо убрать "!=".

    Моржовый оператор очень неоднозначный и непродуманный. Я сам его начал использовать, но мне он не нравится. Уже много обсуждали его при имплементации PEP.

    Лично мое мнение - хватило бы и оператора присваивания, если так уж нужно было добавить эту функциональность. Но раз уж сделали, непонятно почему не сделали нормально:

    • if not x := get_value(): - нельзя

    • if x:= get_value() and something: - нельзя

    • `x = 4 + y := 8` - нельзя (и не надо, но хотя бы было в едином стиле)

    Вообще Питон движется куда-то не туда ИМХО. Я считаю что ни к чему были и f-строки, и моржовый оператор и тот ад что будет в Python 3.10 после PEP 634 (Structural Pattern Matching).

    • baldr
      /#23003120

      Поправка: все что нельзя тоже решается скобочками, но выглядит все равно плохо (ИМХО)

    • zazar
      /#23006626 / +1

      f-строки куда как удобнее и читабельнее прошлых вариантов.

      • baldr
        /#23006758

        Ну, отчасти соглашусь с вами, но далеко не везде они так уж удобнее.

        print("Hello %s" % name)

        print(f"Hello {name}")

        TEMPLATE = "Hello {name}"
        print(TEMPLATE.format(name=name))

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

        Третий вариант наиболее правильный академически - вы определяете где-то шаблон, а потом его несколько раз используете с подстановкой. И как раз этот вариант f-строки совсем не решают. И format() и %-подстановка - решают.

        • masai
          /#23010510

          Третий вариант для данной конкретной задачи формирования одной строки с подстановкой выглядит намного хуже первых двух.

          А для переиспользуемых шаблонов никто f-строки не предлагает использовать.

  4. unsignedchar
    /#23003988

    На python можно писать на С. Ок. Но зачем для этого целый специальный оператор?

    • masai
      /#23010558

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

      • inoyakaigor
        /#23011128

        А объясните суть этого? Почему именно 5 == x?

        • masai
          /#23011312 / +1

          Если опечататься и написать if (x=5) {...}, то это скомпилируется, но будет работать неправильно. Если ставить число впереди, то при подобной опечатке будет ошибка компиляции.

          • DollaR84
            /#23012390

            Ну почему только в C. я и в python предпочитаю так писать, хоть у меня эта привычка и перешла из C/C++ в python, но и тут же данный способ может помочь от опечатки. Конечно скрипт запустится, но в таком случае ошибка выпадет в рантайме, но выпадет, чем уже облегчит поиск проблемы, чем когда произойдет присваивание, никакой ошибки не выпадет, но скрипт будет работать не правильно.

            • masai
              /#23013160 / +1

              В Python это неактуально. if x = 5 и if 5 = x одинаково некорректны, и скрипт не запустится.

              • DollaR84
                /#23013280

                да, точно, спасибо. Проверил, таки не актуально. Значит действительно просто старые привычки

  5. Zoolander
    /#23005354

    как отметили выше, выглядит как лишнее умножение сущностей

    уже есть x = y = z = 12
    то есть подобный смысл у обычного оператора был, надо было просто его расширить

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

    И да, я знаю, что напишу кучу ерунды для стороннего наблюдателя. Такова драма разработки.

    • masai
      /#23010546

      Если прочитаете PEP-572, там в конце, как обычно, есть раздел с обсуждением, где рассматриваются альтернативы и доводы против. Среди прочего там есть раздел, в котором написано, почему не стоит использовать знак присваивания, а нужен новый оператор.

      Основная причина в том, чтобы избежать путаницы между присваиванием и сравнением. Это может легко привести к труднонаходимым ошибкам. В языке C через это уже проходили.

  6. masai
    /#23010526 / +1

    Если мы попытаемся сделать то же самое с помощью обычного оператора присваивания, то получим ошибку типа, поскольку num = 15 ничего не возвращает.

    Мы получим ошибку типа не поэтому, а потому что print(num=15) синтаксически выглядит как передача функции именованного параметра.

    Какой-нибудь print(end="15") никакой ошибки не выдаст, так как = здесь — это не присваивание.