Как стать автором
Обновить

Комментарии 56

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

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

```

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

// doing something with some

}

```

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

```

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

// doing something with some

}

```

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

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

Вы пишете в блокноте?

Ну это был типа "сарказм" (забыл табличку повесить). У меня и в блокноте детектор поднимет панику "эту строчку надо посмотреть внимательней !!!" без всякой подсветки.

А в питоне наверное просто решили что один оператор должен делать одну вещь. Это правильно с одной стороны. А с другой - в куче языков if проверяет не значение, а результат выполнения операции.

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

В Си.

В кашу

В С++ ?

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

В Паскале не было присваиваний в выражениях же. А значок := ещё в Алгол был.

Кстати, Алгол — это очень интересный язык, который в каких-то аспектах опередил время. Очень много других языков заимствовали идеи из него.

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

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

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

yyy: каком?

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

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

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

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

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

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

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

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

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

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

что звучит куда более комично, чем "оператор моржа"

Так английский оригинал и сам комичный, это не научный термин. Я как-то слышал, как графический движок называли графическим двигателем (я не шучу!), так как движок звучит несерьёзно.

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

С удовольствием послушал бы его аргументы. Увы, одной апелляции к авторитету мало.

Да, согласен, что одной апелляции к авторитету мало. Конкретно в этом случае дело скорее в том, что именно по этой книге учатся. И это не книга "какого-то независимого автора", в которую он мог налепить чего угодно. Она задаёт некий профессиональный стандарт (и соответствует ему). Иными словами, именно этот перевод претендует на то, чтобы стать одним из устоявшихся. Просто потому, что студенты, которые учатся на изданиях и переизданиях книги, по-русски потом будут говорить "оператор моржа" и считать именно этот термин нормальным в профессии. Это один из путей формирования языка.

Безотносительно правильности того или иного перевода. Это же не единственная публикация по Python на русском, чтобы задавать какие-то стандарты. Уже есть много переводов. Например, в этой статье.

Да и рекомендация министерства мало на что не влияет. В университете в отличие от школы не раздают учебники. Студенты в праве сами читать, что хотят. И часто это англоязычная документация.

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

Я не берусь утверждать, какой из вариантов приживётся. Просто считаю, что одна книжка (или даже несколько) ничего не изменит.

Во времена паскаля это были «ноздри». В шутку, конечно же.

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

# 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

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

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

То что сам пример упрощён не говорит о том, что морж в нём не уместен.
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]}")

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


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

Кто же спорит.
Лично я в данном случае не стал бы использовать функции Any и All, а написал что-то вроде:
for i, number in enumerate(numbers):
    if number > 10:
        print(f"{number} > 10 (index {i})")
        break
else:
    print("all numbers < 10") 
result = next((n for n in numbers if n > 10), None)

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

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


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

А что плохого в комбинировании? Можете сослаться на какой-нибудь PEP?
result = [x for x in (func(y) for y in data) if x]

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

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

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

Читабельность безусловно становится лучше, но ваш вариант с 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

`value = next((n for n in numbers if n > 10), None)` ?

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


На 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. Но что-то Мартин как-то про это забыл.

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

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

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

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

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

Что-то я не понял, что вы хотели сказать своим примерном с function_call()?
Чем это отличается от function_call(first_arg_in_args=xxx, second_arg_in_args=yyy)?

https://docs.python.org/3/tutorial/controlflow.html#special-parameters

Можно запретить передавать аргументы через ключ значение. потому для вот такого определения функции:

def function_call(xxx,yyy, /, *args, **kwargs ): ...

вызов

function_call(first_arg_in_args=xxx, second_arg_in_args=yyy)

сгенерит ошибку. потому как xxx и yyy должны были быть переданы как аргументы. но не в случае если это будет моржом.

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

other_function_call(first=xxx, second=yyy, third, foutth, new_key=val, ... )

такой вызов не сработает. positional argument follows keyword argument (syntax-error)

А с моржом сработает. И вызов сделали и "задокументировали". Дичь конечно. Но мне встречалось.

Понял, спасибо. Пример немного странный все же, но какой уж есть.

Для меня морж оказался полезен в ситуациях, когда нужно оперировать файлами в еще не созданных директориях (типа создавать дерево проекта и т.д.)

(new_file_path := some_dir_path / filename).parent.mkdir(exists_ok=True)
with open(new_file_path, "wt") as f:
    f.write(some_str)

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

И стало явное не явным!

Полезная статья про написание красивого и читабельного кода. Спасибо.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий