LINUX.ORG.RU

Чем плохо много return?

 ,


0

3

В линтерах во многих языках программирования есть такая проверка: https://docs.astral.sh/ruff/rules/too-many-return-statements/

По ссылке приведён тривиальный пример, где достаточно сопоставления по ключу.

В общем случае код выглядит примерно как:

def f(x):
  if cond1(x):
    return res1
  y = process1(x);
  if cond2(y):
    return res2
  if cond3(y):
    return res3
  z = process2(x, y)
  if cond4(x,y,z):
    return res4
  if cond5(x,y,z):
    return res5
  ...  

после убирания return получается что-то вроде

def f(x):
  done = False 
  if cond1(x):
    res = res1
    done = True
  if not done:
    y = process1(x);
  if not done and cond2(y):
    res = res2
    done = True
  if not done and cond3(y):
    res = res3
    done = True
  if not done:
    z = process2(x, y)
  if not done and cond4(x,y,z):
    res = res4
    done = True
  if not done and cond5(x,y,z):
    res = res5
    done = True
  ...
  return res  

Второй вариант действительно легче читается или я неправильно преобразовал первый во второй?

UPD: С учётом наличия elif

def f(x):
  done = False 
  if cond1(x):
    res = res1
    done = True
  if not done:
    y = process1(x);
    if cond2(y):
      res = res2
      done = True
    elif cond3(y):
      res = res3
      done = True
  if not done:
    z = process2(x, y)
    if cond4(x,y,z):
      res = res4
      done = True
    elif cond5(x,y,z):
      res = res5
      done = True
  ...
  return res  

Вот это читается легче, чем первый вариант?

★★★★★

Последнее исправление: monk (всего исправлений: 1)

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

hibou ★★★★★
()
Ответ на: комментарий от monk

Можно сделать «лесенку вложенных условий» неявной. Например some->, прерывает цепочку вычислений, получив nil. В примере ниже код выглядит линейным, но на самом деле там та самая лесенка вложенных if→elif→elif и до декремента вычисление просто не дойдёт.

(some-> 4
  inc
  println
  dec)
5
nil

Можно реализовать и более сложное поведение.

ugoday ★★★★★
()
Ответ на: комментарий от u5er

«а вот тут собаки и выкопались»

должен легко читаться и пониматься

ибо лёгкость чтения понимания контекстно зависит от выборки читателей_понимателей

так что ща самостоятельно не то что прогер не может отпроизводить x в степени x - но и поотдельности «школьные» степеную и показательную - ибо прогеры в основном не вкуривают фундаментальность массива как переписываемой памяти ака переопределяемой функции отражающей (адрес,момент_исполнения в координатах предложенных Дейкстрой в Goto Considered ну-ты-пон) в нечто

т.е. общее решение очевидно

дидактично азбучно

и раздувание штата пихоты грабящей койрованы и прочие накрутчики

qulinxao3 ★☆
()
Последнее исправление: qulinxao3 (всего исправлений: 1)
Ответ на: комментарий от masa

в петухоне предпочли пойди на поводу «спонтанных» ожиданий

что в тренде текущих завлекающих стратегий успеха

что нынешняя семантика ретурн в финале что прежняя вполне приемлемы кста

забавна смена стула для снижения фрикций

qulinxao3 ★☆
()
Ответ на: комментарий от firkax

если 40 блоков - то явно что то вырвиглазно запроектировано

т.е. тут если и возможно универсальное решение то явно с большим количеством эвристик

40 блоков явно потребно рефачить

qulinxao3 ★☆
()
Ответ на: комментарий от Lrrr

если ты добавишь какой-нибудь условный malloc/free, то в первом варианте тебе придется добавить этот free перед каждым return

Получается, для Python это не актуально. Но для Си где был malloc интуиция быстро кричит что контролить free труднее

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от Bfgeshka

Множественные проверки делаются с помощью операторов «и» и «или».

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от qulinxao3

требую пояснительной бригады чем плох сей шедеврь:


def f(x):
    return res1 if cond1(x)\
     else  res2 if cond2(y:=process1(x)\
     else  res3 if cond3(y)\
     else  res4 if cond4(x,y,z:=process2(x,y))\
     else  res5 if cond5(x,y,z)\
     else  float('inf')/0
qulinxao3 ★☆
()
Ответ на: комментарий от vbr

Короче, особое отношение к return в контроле передачи данных. Поэтому и желательна одна точка выхода.

Это не касается обработки ошибок типа if(..) return -1. Обработку ошибок следует рассматривать отдельно от бизнес-логики.

Psilocybe ★★★★★
()
Ответ на: комментарий от vbr

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

Почему, всё понятно. Дейкстра запретил питоновский yield.

legolegs ★★★★★
()

для Сяшки варинт

loop успех()

в цикле switch fsm для выделения ресурсов и отдачи если что то пошло не так и ветви успехов

как деды завещали

qulinxao3 ★☆
()

Как по мне, то в первом варианте хороший код. Линейный, легко читается последовательно.
Дока линтера немного о другом.

urxvt ★★★★★
()
Ответ на: комментарий от mogwai

Тогда я хызы что ты курил, когда придумал имя «Done» переменной.

Все действия завершены (done) и надо доползти до конца выполнения.

monk ★★★★★
() автор топика
Ответ на: комментарий от ei-grad

Считаем константами из внешнего кода.

monk ★★★★★
() автор топика
Ответ на: комментарий от ei-grad

лучше переписать декларативно, через набор правил и штуку которая единообразно их обработает

Вот! Через что писать код в стиле «сделать шаг Н, проверить что получилось, если плохо вернуть промежуточный, иначе сделать шаг Н+1, …».

monk ★★★★★
() автор топика

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

def f(x):
    REQ_Y=True
    REQ_Z=True
    conds = (
        (cond1, None,  None,  res1),
        (cond2, REQ_Y, None,  res2),
        (cond3, REQ_Y, None,  res3),
        (cond4, REQ_Y, REQ_Z, res4),
        (cond5, REQ_Y, REQ_Z, res5),
    )
    y=None
    z=None
    for cond,ry,rz,res in conds:
        if ry and not y: y=process1(x)
        if rz and not z: z=process2(x,y)
        if cond(x=x,y=y,z=z):
            return res
    return 'nope'

PS я не настоящий питонист

legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 1)
Ответ на: комментарий от mogwai

Но нет, мы будем гору return городить там где не надо. Или велосипеды изобретать.

Потому что это не ошибки. Это штатные варианты ответа. Условно HTTP 200, 201, 204, 301, 302, …

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

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

legolegs ★★★★★
()
Ответ на: комментарий от qulinxao3

требую пояснительной бригады чем плох сей шедеврь:

Палочками в конце строк.

И незакрытой скобкой.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

legolegs кстати был такой дедо-метод

ТАБЛИЦЫ РЕШЕНИЙ

https://en.wikipedia.org/wiki/Decision_table

в СССР в серии Мат Обес ЭВМ книжка:

Хамби Э. Программирование таблиц решений. 1973

на нерусских языках до сих пор вроде тема жива

qulinxao3 ★☆
()
Ответ на: комментарий от monk

Выглядит как обычный reduce, но с ранним выходом. Т.е. что-то в стиле

def stepper(exit_condition, result, steps):
    if exit_conditions or empty?(steps):
        result
    elif:
        //выполняем текущий шаг
        exit_condition, result = steps[1](result)
        stepper(exit_condition, result[1:])

Где steps — список функций — шагов алгоритма.

ugoday ★★★★★
()
Ответ на: комментарий от monk

незакрытая скобка это ваще \эгида дня

палочки \это опасная возможность не нужно злоупотреблять сей.

в данном случае уместно ибо

как тут хэйтеры лесенки питона и фонаты произвольного форматирование(через скобки) многократно сообщали - форматирование позволяет увидеть логику которая чиста как слеза

так что имхо есть ещё какие недостатки?!

qulinxao3 ★☆
()
Ответ на: комментарий от ya-betmen

Зойчем это нужно если уже есть res?

А с чем его сравнивать? Какое значение не может быть равно ни одному результату функции?

monk ★★★★★
() автор топика
Ответ на: комментарий от Chiffchaff

Иногда сижу на копролите, стараюсь не тащить лишний интерпретатор. А так да, все по делу, спасибо за поправку

Morin ★★★★★
()
Ответ на: комментарий от qulinxao3

Деды знали толк в управлении сложностью.

legolegs ★★★★★
()
Ответ на: комментарий от ugoday

Выглядит как обычный reduce, но с ранним выходом. Т.е. что-то в стиле

Получается, что каждая функция читает-пишет все переменные. Почти глобальное состояние. И отлаживать такого крокодила тоже то ещё мучение, если он упал где-то в районе cond4.

monk ★★★★★
() автор топика

Различия на таком уровне проявляются, когда происходит некая неочевидная обработка. Тогда даже два независимых if-else бывает лучше объединить в одну.

В данном конкретном случае это не критично.

kaldeon
()

Там приведён конкретный пример случая, где где словарь лучше и стилистически, и синтаксически. В общем случае — ничем, главное не злоупотреблять как yandredev.

Второй вариант, конечно, плохо-плохо. В этом случае несколько return чище и читабельней.

thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 1)
Ответ на: комментарий от qulinxao3

в чём жи проблема то?!

В глобальности. Одно дело, когда

  if cond1(x):
    return res1
  y = process1(x);
  if cond2(y):
    return res2

И слегка другое, когда

  if this.cond1():
    return res1
  this.process1();
  if this.cond2():
    return res2

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

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

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

т.е имхо для не Си -алгол языков - в том же Питоне вполне приемлимо служебные части выделять во вложенные def которые действуют в контексте обьемлющей материнской функции - без всяких извратов продолжений - чисто дедовские внутрение функции

можно ваще отказатся от nonlocal передавая явно в внутрении функции словари аргументов

qulinxao3 ★☆
()

Чем плохо много return?

Ничем.

ЗЫ: За употребление понятия «антипаттерн» применительно к программированию надо лупить канделябром по тупой наглой харе до кровавых соплей, а потом пинать ногами.

Stanson ★★★★★
()
Последнее исправление: Stanson (всего исправлений: 1)
Ответ на: комментарий от token_polyak

Да, кстати за «бестпрактисы» тоже надо канделябром и ногами.

Stanson ★★★★★
()
Ответ на: комментарий от monk

Во первых можно сбрасывать res.

А во-вторых, функция через одно место написана, вот и проблемы


def res1(x):
   if cond1(x)
       return res1
   else
       return nil
...

res = res1(x)
if res == nil
  res = res2(x) 
if res == nil
  res = res3(x, y) 
... 
   
ya-betmen ★★★★★
()
Ответ на: комментарий от Psilocybe

а Дейкстра с ней боролся

Дейкстра боролся со вполне конкретной практикой тех времён — написанием лапши из глобальных переменных и безусловных прыжков. Религиозные фанатики с мышлением слоганами, конечно же (как и полагается фанатикам) уже после и не вникая в контекст, побежали крестовым походом выпиливать неверный goto отовсюду откуда можно и нельзя.

thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 1)
Ответ на: комментарий от monk

Получается, что каждая функция читает-пишет все переменные.

Откуда? В том примере, функция-шаг получает одно значение — результат предыдущего шага и возвращает два: ошибку и новое значение. Если этого мало можно явно передать словарь с нужными данными. Соответственно

если он упал где-то в районе cond4

Каждый шаг можно отдельно вызвать и посмотреть как и на каких данных он падает.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

В том примере, функция-шаг получает одно значение — результат предыдущего шага и возвращает два: ошибку и новое значение.

Так про что и речь. То, что в первый cond1 уходит только x, а y берётся из process1, из такой формы совершенно не видно.

monk ★★★★★
() автор топика
Ответ на: комментарий от PolarFox

Именно так. Поэтому я захотел узнать, кому так читабельнее.

Потому что видел проект с включенным ограничением и, соответственно, переводом всего кода во вторую форму. Там еще ifelse без else запрещён был и пустые блоки, в результате много кода типа

if cond1:
  ...
elif cond2:
  ...
else
  if cond3:
    ...
monk ★★★★★
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.