Pull to refresh

Comments 24

Не соглашусь. Функция может вернуть True, если операция завершилась успешно

Это разве соответствует идеологии питона? На это есть исключения, код возврата в питоне не особо то применяют.
Исключение должны бросаться в исключительной ситуации. Тут ИМХО автор пытался продвинуть код стайл:
1. Если в функции допустим провал и она ничего не возвращает верни True в случае успеха и False в случае провала.
2. Если нечего вернуть отрапортуй об успехе
Ну так если функция допустила провал, это как раз таки исключительная ситуация и надо что то делать с этим. Или мы немного о разном говорим?!

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

Не очень хороший пример. Получается, что у функции две «ответственности», как выражается автор. Она отправляет метрики и как-то реагирует на невозможность их отправки (игнорирует, логирует warning, считает попытки прежде чем дропнуть исключение?)
При более правильном дизайне наша функция должна ТОЛЬКО отправлять метрики на удалённый сервер и валиться с исключением, если это не удалось. Если вдруг нужно делать несколько попыток или считать неудачи прежде чем бить тревогу, то этим займётся специальная обёртка, декоратор, или какая-то конструкция в вызывающем коде.

Да, автор не затронул тему ООП, мктодов, инкапсулированного состояния и прочее. Все эти правила нужно знать и понимать, но всё было бы слишком просто, если бы это были железные правила и у них бы не было исключений.
Она нарушит single responsibility только если реализует
некоторую функциональность отличную от контракта не бросать эксепшены
Я писал примерно про такой шаблон:
def foo():
  ...
  raise SomeException("Expected Fail")
  ...

def try_foo():
  try:
    foo()
  except:
    return False
  return True

Предвосхищая замечание. Да это по хорошему декоратор

Всё-таки, идемпотентность, не очень удачный термин в данном контексте. Речь идет о чистоте функции или прозрачности по ссылкам, но слово "идемпотентность" кроме труднопроизносимости имеет совсем другой смысл. Это когда f(f(x))=f(x).

Судя по всему, детерминированность подходящий термин.

> Во-первых, слово «get» избыточно. В большинстве грамотно поименованных функций сразу понятно, что данная функция что-то возвращает, что конкретно – отражено в имени.
Вот уж нет. Классический пример это STL с его функциями 'clear' и 'empty'. Одна из этих функций очищает контейнер а другая говорит пуст ли он.

>Поэтому данную функцию лучше переписать в виде двух отдельных функций: одна будет выполнять вычисления и возвращать их результаты, а другая – принимать эти результаты и выводить их в консоль.
А потом написать третью функцию которая вызовет обе эти функции ( потому что у меня в коде они используются очень часто и всегда одна следом за другой), и хммм как бы мне ее назвать…
Вернуть True, если операция завершилась успешно

Какой-то маниакальный бред мне кажется. Зачем вообще что-то возвращать и потом 90% времени это даже не проверять???
Ну т.е. для функций где ничто не может пойти не так (не считая исключений), например:
def calcTotal(price):
    self.total += price

Предлагается добавлять «return True» только на том основании что «ну Python всё равно уже что-то возвращает, давайте это „что-то“ сделаем „полезным“». Мне кажется это уже какой-то маразм…

Мне казалось что вообще проверять возвращаемое значение на предмет ошибки в ООП уже давно моветон… Ну возможно за исключением случаев ввода/вывода.

ЗЫ: Хотя судя по остальным «добрым советам», автор текста просто перепутал парадигму языка и искренне считает python функциональным. Я конечно не против такого использования, но говорить что оно «единственно верное», это всё-же перебор…
Поэтому, извлечение нескольких строк кода из длинной функции и превращение их в самостоятельную функцию – это один из типов рефакторинга.
В результате программа рискует превратиться из набора длинных спагетти (что, безусловно, зло) в маловразумительную кастрюлю вермишели. Особенно это доставляет, когда эта вермишель ещё и разбросана по десятку-другому модулей.

Кроме того, когда функций много, всё сложнее становится придумывать им вразумительные имена. Такие, из которых понятно не только что функция делает, но и зачем она это делает.
Понятие чистоты функции полезно в функциональных и процедуных подходах. В ООП оно — зло. Объект — это по определению состояние, и функции, изменяющие объект, меняют его состояние. Когда логически объект тот-же, но с изменениями, а физически — каждый раз новый, чтобы добится чистоты — то на этот объект очень сложно ссылаться, да и рефакторинг его затруднён.

Гигиеническую роль чистоты в ООП играет принцип инкапсуляции, так что он и там есть и используется.

Аббревиатура – это проблема, поскольку зачастую она специфична для предметной области

Думаю, почти любой небиблиотечный код специфичен для предметной области и это нормально, если человек с улицы его не понимает. Разворачивать все аббревиатуры тоже зло. get_knn_from_df — на мой взгляд, очень понятно, даже для меня (я не data scientist)
UFO just landed and posted this here
UFO just landed and posted this here
Этот вариант, конечно, лучше, но объективно же возможна ситуация, когда преобразовывать из разных входных форматов к единому для вычисления или вовсе невозможно или сложнее, чем сделать два отдельных вычисления на основе разных представлений.
К примеру мы делаем функцию, которая формирует специального вида хеш, вроде контент-id. И специфика алгоритма хеширования сильно зависит от формата входных данных, хотя сам результат — это просто GUID. Этот хеш нужно считать одним способом для звука, другим для изображений и третьим для видео. Если делать тут специальный слой абстрации нецелесообразно (не все же пишут на java, там лишний слой абстракции никогда не лишний=), то сделать три реализации вполне логично.
В питоне же, кстати, нет сигнатурной перегрузки функций, если опираться на названия входных параметров, то код функции может неоправданно усложниться.

Да простят мой тонкий троллинг те, кого он затронул. Хочу лишь добавить, что все правила важны и нужны, а с опытом мы понимаем мета-правила и приобретаем знания где какие правила актуальны.
Непонятно при чем тут питон. Тот же Боб Мартин писал про чистый код в целом (на примере java). Также из названия k_nearest_neighbors я не могу понять что делается с ближайшим соседом. Значит мне придется смотреть в код. Если бы тут присутствовал get мне бы этого делать не пришлось.
Понятно что хотел сказать автор, понятно что хотел сказать Боб. Залезть же в бутылку всегда можно ещё глубже и дно тут не станет ограничением. Возможно кому-то покажется необходимым написать целое эссе в названии функции. Аббревиатуры полезны, если они устоявшиеся; 'get_' может оказаться полезным, если реально убирает какую-то неоднозначность… хотя если у вас есть какая-то неоднозначность, это незначит, что её следует решать только и именно неймингом. Может быть что-то пошло не так раньше, на более ранних этапах проектирования.

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

Всё у вас хорошо. Это у автора «функциональная парадигма головного мозга» :)

Если это не класс коннектор то стоит его таким сделать. И использовать либо наследованием либо композицией с DI в классе где вы используете коннект к базе. ФП безусловно пресутствует в языке, но в таких случаях вы прям сильно теряете, выбирая его вместо ООП

Закрытие лучше не пихать в метод явно, а делать через context manager.

И если у вас в этом методе нет никакой обработки данных, то это как раз хороший подход, т.к. тестировать такой метод особой нужды нет.
Sign up to leave a comment.