Сегодня рассказываем о самом странном операторе Python — операторе моржа. Для чего он нужен, и как использовать его с учётом других особенностей языка? Подробности к старту курса по Fullstack-разработке на Python — под катом:



Оператор присваивания (или оператор моржа) появился ещё в Python 3.8, но он всё ещё вызывает споры, а многие необоснованно его ненавидят. Я же попытаюсь убедить вас, что этот оператор — хорошее дополнение к языку, а при правильном применении он может помочь сделать код короче и яснее.

Основы


Посмотрим на основные варианты применения оператора моржа, которые могут убедить вас попробовать, если вы ещё не знакомы с :=.

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

Представим функцию func(), которая выполняет очень ресурсоёмкие вычисления. Её работа занимает много времени, поэтому вызывать функцию много раз не хочется:

# "func" вызывается три раза
result = [func(x), func(x)**2, func(x)**3]

# Используем результат "func" в одну строку, без многострочного кода

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

В объявлении первого списка выше func(x) вызывается трижды. Каждый раз возвращается один и тот же результат — и это пустая трата времени ресурсов. При перезаписи с оператором моржа func() вызывается только один раз: её результат присваивается y и повторно используется для оставшихся значений списка. Вы можете возразить: «Морж не нужен, я могу просто добавить y = func(x) перед объявлением списка!» Да, но это лишняя строка кода, и на первый взгляд — не зная, что func(x) работает очень медленно, — может быть непонятно, для чего нужна переменная y.

Если это не убедило вас, есть ещё кое-что. Вот списковое включение с той же дорогостоящей func():

result = [func(x) for x in data if func(x)]

result = [y for x in data if (y := func(x))]

В первой строке функция func(x) в каждом цикле вызывается дважды. С оператором моржа функция вычисляется один раз, внутри if, а затем результат используется повторно. Длина кода одинаковая, обе строки одинаково читаемы, но вторая в два раза эффективнее. Производительность можно сохранить, заменив оператор моржа на полный цикл for, но для этого потребуется 5 строк кода.

Один из самых распространённых вариантов применения оператора моржа — сокращение вложенных условий, например в сопоставлении при работе с регулярными выражениями:

import re

test = "Something to match"

pattern1 = r"^.*(thing).*"
pattern2 = r"^.*(not present).*"

m = re.match(pattern1, test)
if m:
    print(f"Matched the 1st pattern: {m.group(1)}")
else:
    m = re.match(pattern2, test)
    if m:
        print(f"Matched the 2nd pattern: {m.group(1)}")

# ---------------------

# Чище

if m := (re.match(pattern1, test)):
    print(f"Matched 1st pattern: '{m.group(1)}'")
elif m := (re.match(pattern2, test)):
    print(f"Matched 2nd pattern: '{m.group(1)}'")

Код сократился с 7 до 4 строк, а за счёт удаления вложенного if стал более читаемым.

Следующая в списке — идиома «loop-and-half» («цикл пополам»):

while True:  # Цикл
    command = input("> ")
    if command == 'exit':  # и пополам
        break
    print("Your command was:", command)

# ---------------------

# Чище

while (command := input("> ")) != "exit":
    print("Your command was:", command)

Обычное решение — фиктивный бесконечный цикл while, в котором поток управления передаётся оператору break. Но также можно задействовать оператор моржа, чтобы переназначить значение command, а затем использовать его в условном цикле while в той же строке. Это сделает код намного чище и короче.

Аналогичное упрощение применимо и к другим циклам while, например при чтении файлов построчно или при получении данных из сокета.

Накапливание (аккумулирование) данных на месте


Перейдём к более сложным случаям применения оператора моржа:

data = [5, 4, 3, 2]
c = 0; print([(c := c + x) for x in data])  # c = 14
# [5, 9, 12, 14]

from itertools import accumulate
print(list(accumulate(data)))

# ---------------------

data = [5, 4, 3, 2]
print(list(accumulate(data, lambda a, b: a*b)))

# [5, 20, 60, 120]

a = 1; print([(a := a*b) for b in data])

# [5, 20, 60, 120]

Первые две строки показывают, как использовать := для вычисления промежуточного результата. В настолько простом случае лучше подойдёт функция itertools, например accumulate, как в двух следующих строках. Но в ситуациях сложнее itertools довольно быстро становится нечитаемым, и, на мой взгляд, версия с := намного лучше, чем с lambda.

Если вы всё ещё не уверены, ознакомьтесь с нечитабельными примерами в документации accumulate (с накоплением процентов или логистическим отображением). Попробуйте переписать их с оператором моржа, они станут выглядеть намного лучше.




Именно так, шаг за шагом, вы станете востребованным профессионалом в области IT:


Именование значений внутри f-строки


Этот пример показывает возможности и ограничения :=, но не лучшие практики.
Если очень хочется, := можно использовать внутри f-строк:

from datetime import datetime

print(f"Today is: {(today:=datetime.today()):%Y-%m-%d}, which is {today:%A}")

# Today is: 2022-07-01, which is Friday

from math import radians, sin, cos
angle = 60
print(f'{angle=}\N{degree sign} {(theta := radians(angle)) =: .2f}, {sin(theta) =: .2f}, {cos(theta) =: .2f}')

# angle=60° (theta := radians(angle)) = 1.05, sin(theta) = 0.87, cos(theta) = 0.50

В первом print оператор := определяет переменную today, которая затем используется в той же строке; today избавляет нас от повторного вызова datetime.today().

Точно так же во втором примере объявляется переменная theta, которая повторно используется для вычисления sin(theta) и cos(theta). В этом случае мы также используем его в сочетании с оператором «обратный морж» — на самом деле это просто =, с ним выражение выводится рядом с его значением, а : используется для форматирования выражения.

Обратите внимание: чтобы f-строка правильно интерпретировала выражения с оператором моржа, их нужно заключать в круглые скобки.


Any и All


Чтобы проверить, удовлетворяют ли какие-то значения в некотором итерируемом объекте определённому условию, можно восопользоваться функциями any() и all(). Но что делать, если нужно зафиксировать значение, из-за которого any() вернуло True (так называемое «свидетельство»), или значение, которое привело к сбою all() — «контрпример»?

numbers = [1, 4, 6, 2, 12, 4, 15]

# Возвращает только логические значения, а не обычные

print(any(number > 10 for number in numbers))  # True
print(all(number < 10 for number in numbers))  # False

# ---------------------

any((value := number) > 10 for number in numbers)  # True
print(value)  # 12

all((counter_example := number) < 10 for number in numbers)  # False
print(counter_example)  # 12

И any(), и all() для вычисления выражения используют вычисление по короткой схеме, то есть прекращают вычисления, как только находят первое «свидетельство» или «контрпример» соответственно. А значит, при таком трюке созданная оператором моржа переменная всегда будет возвращать первого «свидетеля» или «контрпример».

Подводные камни и ограничения


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

В предыдущем примере вы видели, что вычисление по короткой схеме полезно для захвата значений в any()/all(), но иногда это может привести к неожиданным результатам:

for i in range(1, 100):
    if (two := i % 2 == 0) and (three := i % 3 == 0):
        print(f"{i} is divisible by 6.")
    elif two:
        print(f"{i} is divisible by 2.")
    elif three:
        print(f"{i} is divisible by 3.")

# NameError: name 'three' is not defined [имя 'three' не определено]

Мы создали условие с двумя соединёнными and присваиваниями. Эти and проверяют, делится ли число на 2, 3 или 6 в зависимости от того, выполняются ли первое, второе или оба условия. На первый взгляд это может показаться хорошим приёмом, но из-за вычисления по короткой схеме, если выражение (two: = i % 2 == 0) не выполняется, вторая часть будет пропущена, а значит, имя three окажется неопределённым или будет иметь устаревшее значение из предыдущего цикла.

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

import re

tests = ["Something to match", "Second one is present"]

pattern1 = r"^.*(thing).*"
pattern2 = r"^.*(present).*"

for test in tests:
    m = re.match(pattern1, test)
    if m:
        print(f"Matched the 1st pattern: {m.group(1)}")
    else:
        m = re.match(pattern2, test)
        if m:
            print(f"Matched the 2nd pattern: {m.group(1)}")

# Сопоставлено с первым паттерном: thing

# Сопоставлено со вторым паттерном: present

for test in tests:
    if m := (re.match(pattern1, test) or re.match(pattern2, test)):
        print(f"Matched: '{m.group(1)}'")
        # Сопоставлено: 'thing'
        # Сопоставлено: 'present'

Мы уже видели версию этого фрагмента в первом разделе статьи, где в сочетании с оператором моржа использовались if/elif. Здесь код упрощается ещё больше: условное выражение сокращается до единственного if.

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

values = [3, 5, 2, 6, 12, 7, 15]

tmp = "unmodified"
dummy = [tmp for tmp in values]
print(tmp)  # Как и ожидалось, "tmp" не разбился на элементы. Он по-прежему "немодифицированный"

total = 0
partial_sums = [total := total + v for v in values]
print(total)  # Выводит: 50

При обычном списковом включении list, dict или set переменная цикла не просачивается в окружающую область, и поэтому любые существующие переменные с тем же именем не изменятся. Но с оператором моржа переменная из включения (total в приведённом выше коде) останется доступной после завершения включения, она примет значение из внутреннего включения.

Когда вы освоитесь с оператором моржа, то сможете попробовать его в других случаях. Но вот одно место, где вы никогда не должны его использовать, — это оператор with:

class ContextManager:
    def __enter__(self):
        print("Entering the context...")

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Leaving the context...")

with ContextManager() as context:
    print(context)  # None

with (context := ContextManager()):
    print(context)  # <__main__.ContextManager object at 0x7fb551cdb9d0>

С обычным синтаксисом with ContextManager() as context: ... context привязывается к возвращаемому значению context.__enter__(), а если вы используете версию с := — к результату ContextManager(). Часто это не имеет большого значения, ведь context.__enter__() обычно возвращает self, но если это не так, то при отладке возникнут большие проблемы.

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

from contextlib import closing
from urllib.request import urlopen

with closing(urlopen('https://www.python.org')) as page:
    for line in page:
        print(line)  # Вывод HTML-кода страницы

with (page := closing(urlopen('https://www.python.org'))):
    for line in page:
        print(line)  # TypeError: 'closing' object is not iterable [объект 'closing' не итерируемый]

Ещё одна проблема, с которой вы можете столкнуться, — приоритет оператора моржа, ведь он ниже приоритета логических операторов:

text = "Something to match."
flag = True

if match := re.match(r"^.*(thing).*", text) and flag:
    print(match.groups())  # AttributeError: 'bool' object has no attribute 'group' [у объекта 'bool' нет атрибута 'group']

if (match := re.match(r"^.*(thing).*", text)) and flag:
    print(match.groups())  # ('thing',)

Здесь мы видим, что присваивание нужно заключить в круглые скобки, чтобы гарантировать, что результат re.match(...) присваивается переменной. Если этого не сделать, выражение and будет вычисляться первым, то есть будет присвоен логический результат.

И, наконец, не ловушка, а скорее небольшое ограничение. Сейчас с оператором моржа нельзя использовать подсказки встроенного типа. Поэтому, если захочется указать тип переменной, нужно разбить её на 2 строки:

from typing import Optional

value: Optional[int] = None
while value := some_func():
    ...  # Что-то делаем

Заключительные мысли


Оператором моржа, как и любой другой особенностью синтаксиса, можно злоупотребить: снизить ясность и читабельность кода. Не нужно писать его везде, где только возможно. Относитесь к этому оператору как к инструменту — знайте его преимущества и недостатки, а используйте там, где это уместно.

Если хочется увидеть более практичное и эффективное использование оператора моржа, узнайте, как он появился в стандартной библиотеке CPython — все эти изменения можно найти в этом PR. И рекомендую прочитать PEP 572, в котором есть обоснование появления оператора и ещё больше примеров.



image
А мы поможем прокачать ваши навыки или с самого начала освоить профессию, востребованную в любое время:

Комментарии (43)


  1. Vindicar
    19.08.2022 00:36
    +21

    Что я вынес из статьи: оператор моржа можно использовать только для сокращения условий.
    Всё остальное — шуточка из разряда «вставить SQL-запрос в f-строку».


  1. zorn_v
    19.08.2022 00:42
    +5

    Ну незнаю почему он в питоне кажется "странным", в пхп/с/else такие вещи кажутся обычным делом

    ```

    if ($some = $obj->getSome()) {

    // doing something with some

    }

    ```

    Ну или на крайняк

    ```

    if (null !== $some = $obj->getSome()) {

    // doing something with some

    }

    ```

    Потому что = очень похоже на == но тебе об этом никто не скажет )


  1. sedyh
    19.08.2022 01:34
    +2

    Python превращается в Go


    1. randomsimplenumber
      19.08.2022 06:32
      +1

      В Си.


      1. whoisking
        19.08.2022 09:44
        +16

        В кашу


        1. Myxach
          20.08.2022 04:37
          +1

          В С++ ?


    1. vladimirad
      19.08.2022 14:38
      +3

      Что все уже забыли великий и ужасный паскаль? Присваивания. Оператор присваивания. (рассуждения про кальку английского валруса вырезаны внутренней цензурой)


  1. vagon333
    19.08.2022 04:44
    +18

    Код компактнее, действительно, но лично для меня менее читабелен.


  1. Great_Beaver
    19.08.2022 06:17
    +12

    Сразу вспомнился баш:

    xxx: этот язык, хоть и от гугла, никогда не станет успешным, помню о Правиле.

    yyy: каком?

    xxx: Ни один язык с ':=' никогда не достигнет успеха.

    xxx: Это оператор смерти.


  1. koreychenko
    19.08.2022 09:06
    +4

    Я понимаю, что еще нет сложившейся практики перевода этого walrus "моржеприсваивания" на русский язык. Но "оператор моржа" это "ту мач ас фор ми". Вспоминаются гуртовщики мыши (для тех, кто понимает).


    1. iig
      19.08.2022 10:44
      +1

      Думаю, любой перевод (моржевание?) еще хуже чем транслитерация.


    1. stranger777
      20.08.2022 22:39

      Дмитрий Юрьевич Фёдоров, преподаватель программирования и автор вот этой книги о программировании на Python (рекомендовано МинОбр, рецензировалась докторами технических наук), с вами не согласится. Именно в этой книге вы найдёте "оператор моржа".

      Прямо на Хабре по соответствующему запросу был (и есть) вариант "моржовый оператор", что звучит куда более комично, чем "оператор моржа", и даже может вызвать нежелательные ассоциации с известным выражением. :)

      Скорее всего, позже в русском языке устоится перевод морж. Просто морж, как самый краткий (здесь уже есть пример). А пока — даже судя по реакции здесь — люди не очень свыклись с самим оператором моржа как таковым, часто воспринимают его как нечто чужеродное, и многим людям его название трудно сократить чисто психологически.

      Да, как верно заметили, был и есть Pascal, но там этот оператор имеет чётко определённое значение простого присваивания. И, судя по всему, слово оператор пока остаётся у моржа, чтобы подчеркнуть, что это не опечатка (и не что-то, как я уже говорил, чужеродное), а именно оператор.

      Оператор-морж тоже звучит гораздо более странно, ведь мы не называем < или > оператором-птичкой, а = — оператором-рельсами. :) Оператор моржа среди всех вариантов хотя бы обладает оттенком нарицательного. Ну и всё же изначально, когда этому оператору давали название, лёгкую комичность в него закладывали. Она выражается и в переводе.

      P.S. А в книгу, на которую ссылаюсь, автор явно вкладывал душу. И видно по тексту, что своё дело он любит, так что "оператор моржа" там появился явно не оттого, что автор "писал как попало" :)


  1. Shrim
    19.08.2022 09:31
    +11

    Не надо моржа пихать где не попадя, во многих примерах он не нужен.
    Не надо экономить строки в ущерб читаемости.

    # result = [y := func(x), y**2, y**3]
    y = func(x)
    z = 3  # or 100500
    result = [y**e for e in range(1, z+1)]
    
    О ужас, аж две лишних строки кода!
    Зато код легко читаем и изменяем, при изменении количества степеней достаточно изменить значение переменной z

    # result = [y for x in data if (y := func(x))]
    result = [x for x in map(func, data) if x]
    
    Не потребовалось 5 строк кода, код стал даже короче на 2 символа.

    import re
    test = "Something to match"
    patterns = (
        r"^.*(thing).*",
        r"^.*(not present).*",
    )
    for i, pattern in enumerate(patterns, 1):
        m = re.match(pattern, test)
        if m:
            break
    print(f"Matched the {i}st pattern: {m.group(1)}")
    
    И снова код более читаем и гибок.
    В случае добавления паттерна в этом варианте добавится всего одна строка, в приведённом в статье — три строки.

    # print(list(accumulate(data, lambda a, b: a*b)))
    print(list(accumulate(data, operator.mul)))
    
    Иногда нужно читать документацию, чтоб не выдумывать костыли и велосипеды.

    Но я не говорю, что морж не нужен.
    Это инструмент и использовать его надо с умом и по назначению.
    Например в примере с Any и All он вполне уместен.
    any((value := number) > 10 for number in numbers)  # True
    print(value)  # 12
    


    1. lgorSL
      19.08.2022 11:04
      +2

      Например в примере с Any и All он вполне уместен.

      Мне кажется, нет, либо пример надо менять. Во-первых, если список пустой, к value вообще нельзя нельзя обращаться в дальнейшем - оно не определено.
      Во-вторых, если в списке не будет подходящего объекта, value всё равно будет что-то содержать, и надо бы проверять, что вернули функции all или any.


      1. Shrim
        19.08.2022 11:58
        -1

        То что сам пример упрощён не говорит о том, что морж в нём не уместен.
        Fix:

        result = any(
            number > 10
            for i, number in enumerate(numbers)
            if (key := i) is not None  # проверка не нужна, просто сохраняем индекс
        )
        if numbers and result:
            print(f"key:\t{key}\r\nvalue:\t{numbers[key]}")


        1. mayorovp
          19.08.2022 13:03
          +11

          Это не код, а ребус, не надо так делать.


          Добавление побочных эффектов в функциональные конструкции усложняет их понимание больше чем любые "моржеоператоры" в примерах выше.


          1. Shrim
            19.08.2022 13:30
            +2

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

            for i, number in enumerate(numbers):
                if number > 10:
                    print(f"{number} > 10 (index {i})")
                    break
            else:
                print("all numbers < 10") 


        1. lgorSL
          20.08.2022 00:47
          +1

          result = next((n for n in numbers if n > 10), None)


    1. mayorovp
      19.08.2022 11:42

      result = [x for x in map(func, data) if x] Не потребовалось 5 строк кода, код стал даже короче на 2 символа.

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


      Надо или оставаться на чистом List Comprehension, и тут "оператор моржа" поможет — или отказываться от List Comprehension полностью, и вызов map вложить внутрь filter.


      1. Shrim
        19.08.2022 12:05
        +2

        А что плохого в комбинировании? Можете сослаться на какой-нибудь PEP?

        result = [x for x in (func(y) for y in data) if x]


        1. mayorovp
          19.08.2022 12:50
          -3

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


    1. rnb
      19.08.2022 16:34

      >> код стал даже короче на 2 символа
      Тот случай, когда 1 старый меч лучше двух новых граблей ))


    1. dmitrysvd
      20.08.2022 06:06

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


    1. ddastt
      20.08.2022 22:13
      -1

      Читабельность безусловно становится лучше, но ваш вариант с map заставит функцию выполниться несколько раз, в то время как "морж"-вариант ее результат позволяет переиспользовать:

      def test(n = 3):
      ... print('test :', n)
      ... return n + 7
      ...
      y = [x := test(), x2, x3]
      test : 3
      y = [test()**e for e in (1, 2, 3)]
      test : 3
      test : 3
      test : 3


  1. danilovmy
    19.08.2022 09:34
    +14

    Каждый раз, когда я читаю что-то от Мартина Хайнца, я понимаю - Мартин знает много, но не больше. Он копает достаточно, но не глубоко.


    На Python DE 2022 я рассказывал о моржовом операторе с тем же примером [xx := yy, xx2, xx3].
    Но в моем докладе - это был пример, почему мы не должны писать такой код. Все примеры в этой статье Мартина - это шаблоны кода, которые я бы не стал использовать. Кстати, и не только в этой статье у него такое встречается.

    Я согласен, с О-моржовым у нас становится меньше строк кода, но зато в этой части кода невероятно возрастает сложность. Каждая строка начинает делать более чем один 1 тип действий за раз. Каждая строка начинает люто нарушать п.2 из PEP20.

    Кстати, Мартин забыл важный пример для статьи. Как с помощью walrus превратить список аргументов в функции в ключевые аргументы:

    my_result = function_call(first_arg_in_args := xxx, second_arg_in_args:=yyy, ... ) # вместо function_call(xxx, yyy).

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

    p.s. На if с присвоением в питоне сделан pattern matching. Но что-то Мартин как-то про это забыл.


    1. thatsme
      19.08.2022 11:01

      while (command := input("> ")) != "exit":
      print("Your command was:", command)

      А мне наоборот, такой синтаксис нравится ... глядишь, лет ч-з десять ';' введут, и отступы уберут, и совсем питон на C станет похож ...


      1. sukhe
        19.08.2022 21:06

        Опоздали вы с пожеланиями:
        >>> a=10; b=20; c=30; d = a*b*c; print (d)
        6000

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


        1. thatsme
          19.08.2022 23:04
          -1

          Да ладно. Их можно и под что-то другое использовать. Как пример bash, где можно фигурные скобочки использовать для совершенно разных вещей. Это контекстно зависимо. Я в своё время на 1-е апреля для bash патч сделал, для того что-бы then, do, fi, done, заменить фигурными скобками. Тогда как-раз была какая-то уязвимость с этим связана, уже не помню, но фигурных скобочек становилось очень много, что читабельность не уличшало, а безопасность кода просто в негатив выводило. Вот нашёл: https://github.com/pkpro/ebash


  1. Bronx
    19.08.2022 09:37
    +9

    Logical short-cirquit evaluation всё же лучше переводить как "вычисление по короткой схеме", потому что "замыканием" традиционно переводится термин "closure".


    1. stranger777
      20.08.2022 22:45
      +1

      Спасибо! Так, действительно, намного лучше и точнее. Заменил.


    1. mayorovp
      20.08.2022 22:55

      С другой стороны, термин "короткое замыкание" общеизвестен. Переводить общеизвестный термин нестандартным способом тоже не очень правильно.


      1. Bronx
        21.08.2022 01:15

        Можно сказать "логическое закорачивание" или просто "закорачивание".


  1. evgenyk
    19.08.2022 11:27
    +11

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


  1. N-Cube
    19.08.2022 12:22
    -3

    Мало того, что польза оператора сомнительна, так еще и код с ним не будет работать, к примеру, на Google Colab (Python 3.7) и в целом на не самых новых системах Linux. Ради модного оператора сломать работающий код?


    1. anisimovih
      20.08.2022 22:41
      +1

      Вы сейчас реально придрались к фиче из-за того, что она появилась в более новой версии питона, чем та, которая нужна конкретно вам?


      1. N-Cube
        21.08.2022 06:58
        -1

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


        1. mayorovp
          21.08.2022 10:51
          +2

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


          С момента выхода Python 3.8 прошло почти три года! За такое время даже С++ новый стандарт выпускает, и никто не жалуется что они это делают слишком часто.


  1. NeoCode
    19.08.2022 13:06
    +1

    Проблема Питона не в операторе моржа, а в том что объявление переменных синтаксически не отличается от их использования. И вот этот оператор моржа, как я понял из статьи, пытается частично решить эту проблему... Но поскольку решение частичное, оно все равно не решает проблему. Вот если бы обязали объявлять все переменные только оператором моржа, а присваивать в ранее объявленные переменные - обычным присваиванием (как это сделано в Go), вот тогда бы был толк.


  1. Michae_JSON
    19.08.2022 14:28
    +2

    Ребят, я тут новенький, стоит учить моржа? Или дрессировке не поддается на начальном этампе?


    1. danilovmy
      19.08.2022 17:15
      +2

      Если планируешь выступать с моржом и питоном, то, по мне, пока цирк какой-то получается. :)


    1. sukhe
      19.08.2022 21:34
      +1

      Да что там сложного? Этот оператор просто сохраняет в переменную значение выражения для последующего использования. Для каких-нибудь однострочников вполне пойдёт:

      def fib(n): return m if (m:=abs(n))<2 else int(fib(n-2*(z:=n/m)) + z*fib(n-z))

      Ну да — можно расписать красиво на несколько строчек, с комментариями, аннотациями типов и даже юниттестами. Это если куда-нибудь в продакшен.

      А если надо для себя, «на коленке» по быстрому чего-нибудь прикинуть — ну почему нет-то?


  1. Prion
    19.08.2022 23:55

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


  1. orenty7
    20.08.2022 23:03

    Имхо, просто переизобрели классику от K&R

    while((c = getchar()) != EOF) {...}