Comments 24
Не соглашусь. Функция может вернуть True, если операция завершилась успешно
Это разве соответствует идеологии питона? На это есть исключения, код возврата в питоне не особо то применяют.
1. Если в функции допустим провал и она ничего не возвращает верни True в случае успеха и False в случае провала.
2. Если нечего вернуть отрапортуй об успехе
Провал не всегда исключительная ситуация. Функция например отправляет метрики на удаленный сервер. В случае недоступности оного отбрасывает их так как они все равно на момент того как оный подымется будут неактуальны. Мы не хотим тут бросать исключение так как в целом мы ожидаем, что сервер может быть недоступен и мы можем продолжить работу и без него. Или ситауция мы хотим почистить свои временные файлы и не смогли их удалить т.к. была перезагрузка и tmpfs пуст. Функция чистки завершилась провалом, но он ожидаемый и исключительной ситуацией не является и исключение не покинет функцию очистки временных файлов.
При более правильном дизайне наша функция должна ТОЛЬКО отправлять метрики на удалённый сервер и валиться с исключением, если это не удалось. Если вдруг нужно делать несколько попыток или считать неудачи прежде чем бить тревогу, то этим займётся специальная обёртка, декоратор, или какая-то конструкция в вызывающем коде.
Да, автор не затронул тему ООП, мктодов, инкапсулированного состояния и прочее. Все эти правила нужно знать и понимать, но всё было бы слишком просто, если бы это были железные правила и у них бы не было исключений.
некоторую функциональность отличную от контракта не бросать эксепшены
Я писал примерно про такой шаблон:
def foo():
...
raise SomeException("Expected Fail")
...
def try_foo():
try:
foo()
except:
return False
return True
Всё-таки, идемпотентность, не очень удачный термин в данном контексте. Речь идет о чистоте функции или прозрачности по ссылкам, но слово "идемпотентность" кроме труднопроизносимости имеет совсем другой смысл. Это когда f(f(x))=f(x).
Вот уж нет. Классический пример это STL с его функциями 'clear' и 'empty'. Одна из этих функций очищает контейнер а другая говорит пуст ли он.
>Поэтому данную функцию лучше переписать в виде двух отдельных функций: одна будет выполнять вычисления и возвращать их результаты, а другая – принимать эти результаты и выводить их в консоль.
А потом написать третью функцию которая вызовет обе эти функции ( потому что у меня в коде они используются очень часто и всегда одна следом за другой), и хммм как бы мне ее назвать…
Вернуть True, если операция завершилась успешно
Какой-то маниакальный бред мне кажется. Зачем вообще что-то возвращать и потом 90% времени это даже не проверять???
Ну т.е. для функций где ничто не может пойти не так (не считая исключений), например:
def calcTotal(price):
self.total += price
Предлагается добавлять «return True» только на том основании что «ну Python всё равно уже что-то возвращает, давайте это „что-то“ сделаем „полезным“». Мне кажется это уже какой-то маразм…
Мне казалось что вообще проверять возвращаемое значение на предмет ошибки в ООП уже давно моветон… Ну возможно за исключением случаев ввода/вывода.
ЗЫ: Хотя судя по остальным «добрым советам», автор текста просто перепутал парадигму языка и искренне считает python функциональным. Я конечно не против такого использования, но говорить что оно «единственно верное», это всё-же перебор…
Поэтому, извлечение нескольких строк кода из длинной функции и превращение их в самостоятельную функцию – это один из типов рефакторинга.В результате программа рискует превратиться из набора длинных спагетти (что, безусловно, зло) в маловразумительную кастрюлю вермишели. Особенно это доставляет, когда эта вермишель ещё и разбросана по десятку-другому модулей.
Кроме того, когда функций много, всё сложнее становится придумывать им вразумительные имена. Такие, из которых понятно не только что функция делает, но и зачем она это делает.
Аббревиатура – это проблема, поскольку зачастую она специфична для предметной области
Думаю, почти любой небиблиотечный код специфичен для предметной области и это нормально, если человек с улицы его не понимает. Разворачивать все аббревиатуры тоже зло. get_knn_from_df — на мой взгляд, очень понятно, даже для меня (я не data scientist)
К примеру мы делаем функцию, которая формирует специального вида хеш, вроде контент-id. И специфика алгоритма хеширования сильно зависит от формата входных данных, хотя сам результат — это просто GUID. Этот хеш нужно считать одним способом для звука, другим для изображений и третьим для видео. Если делать тут специальный слой абстрации нецелесообразно (не все же пишут на java, там лишний слой абстракции никогда не лишний=), то сделать три реализации вполне логично.
В питоне же, кстати, нет сигнатурной перегрузки функций, если опираться на названия входных параметров, то код функции может неоправданно усложниться.
Да простят мой тонкий троллинг те, кого он затронул. Хочу лишь добавить, что все правила важны и нужны, а с опытом мы понимаем мета-правила и приобретаем знания где какие правила актуальны.
Я тут как начинающий и непрофесионал, пишущий для себя, хочу спросить: а куда же запихивать обращение к файловой системе, бд, сети, если в функцию нельзя. Только вот вчера в одном классе запихал в метод открытия на чтение бд и в нее же закрытие, то же самое с чтением файла. До прочтение этой статьи думал, что это нормально.
Если это не класс коннектор то стоит его таким сделать. И использовать либо наследованием либо композицией с DI в классе где вы используете коннект к базе. ФП безусловно пресутствует в языке, но в таких случаях вы прям сильно теряете, выбирая его вместо ООП
И если у вас в этом методе нет никакой обработки данных, то это как раз хороший подход, т.к. тестировать такой метод особой нужды нет.
Как сделать функции на Python еще лучше