Возможно ли иметь разные значения возврата в одной и той же функции?

#python #python-3.x

Вопрос:

У меня есть эта функция :

 def choice(default=True):
    if default:    
        return 1
    else:
        return 0, "Your choice is 0"
 

В первом случае я получаю одну переменную, во втором случае я получаю две переменные.

Является ли проблемой сделать это в Python ? Я протестировал, и это работает, но, например, в c такие вещи не работают. Вы не знаете, есть ли какое-нибудь правило на этот счет, например, PEP8 или что-то в этом роде ?

Большое спасибо!

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

1. Это просто плохой код. Почему ты хочешь это сделать? Это происходит потому, что вы возвращаете либо int, либо int и строку.

2. Это возможно, но я бы счел это плохой практикой, так как вызывающему абоненту придется защищаться от неожиданных возвращаемых значений.

3. вот что pep8 говорит о возврате: «» » Будьте последовательны в заявлениях о возврате. Либо все операторы возврата в функции должны возвращать выражение, либо ни один из них не должен. Если какая-либо инструкция return возвращает выражение, любые инструкции return, в которых не возвращается значение, должны явно указывать, что это не возвращает никакого значения, и явная инструкция return должна присутствовать в конце функции (если она доступна):»»»

4. Ты мог бы, по крайней мере, сделать так, чтобы первый был return 1, None

5. @schwobaseggl Как это может быть «неожиданно», если вызывающий абонент должен передать опцию для переключения между возвращаемыми значениями?

Ответ №1:

Абсолютно нормально иметь функцию с различными сигнатурами возвращаемых значений, в зависимости от переданных параметров. См., np.unique например. Поскольку Python не имеет перегрузки функций, это канонический способ достижения этой цели. Важно правильно это задокументировать. В примере np.unique в противном случае вам пришлось бы определить восемь различных версий функции со всеми комбинациями возвращаемых значений. Непрактично, сложно в обслуживании и в основном избыточно.

Другой способ справиться с такого рода «перегрузкой функций» в Python-всегда использовать одну и ту же сигнатуру возвращаемого значения, но возвращать None неиспользуемые значения. Мне больше нравится первый метод.

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

1. Можно возразить, что np.unique просто плохо спроектирован. С чем я полностью согласен. Просто потому, что кто-то делает это, не значит, что это хорошо.

2. @freakish Альтернативой является наличие восьми различных функций со всеми возможными комбинациями возвращаемых значений: np.unique_return_inverse , np.unique_return_index_counts и так далее. Как я уже сказал, если кто-то педантично относится к поддержанию сигнатуры возвращаемого значения, есть способы добиться этого, но почему?

3. Это неплохая альтернатива. Но это лучше: возвращаемый тип может быть пользовательским объектом со всеми этими данными внутри, без каких-либо (или какого-либо другого индикатора), если не запрашивается. Причина, по которой мы педантичны в отношении типов возвращаемых данных, заключается в том, что мы не хотим изменять код (после вызова) только потому, что мы изменили аргументы. Также это проще сделать для статического анализа, который в Python всегда сложен по дизайну.

4. «Пользовательский объект», который вы описываете, может быть a tuple , и это то, что возвращает Python, когда вы возвращаете несколько объектов из функции. Я указал это в качестве возможной альтернативы в ответе. 😉 В случае unique , если вы хотите получить другой результат, в этом весь смысл дополнительного варианта возврата. Нет смысла сохранять старый код, потому что вы явно просите семантически другую вещь. Заменить ваши старые звонки не так уж сложно uniq = np.unqiue(a) uniq, inv = np.unique(a, return_inverse=True) .

5. Тот , кого я упоминаю, — это человек, использующий np.unique . Если вы хотите, например, дополнительно получить обратные значения, вам все равно придется изменить строку кода, независимо от того, используете ли вы uniq, inv = np.unique(a, return_inverse=True) или uniq, inv = np.unique_return_inverse(a) .


Ответ №2:

В первом случае я получаю одну переменную, во втором случае я получаю две переменные.

На самом деле в обоих случаях вы получаете один и тот же объект. В первом случае это int, а во втором случае это кортеж (с int и строкой внутри).

Является ли проблемой сделать это в Python ?

Технически? Нет. С точки зрения обслуживания и удобочитаемости? ДА. Можете ли вы представить чье-то удивление, когда он поймет, что изменение аргумента изменяет тип результата (в вашем случае int против кортежа)? Что еще хуже, эти типы совершенно не связаны. Как правило, никто этого не ожидает. А потом им приходится писать странную логику, чтобы справиться с этим. Раздражающий.

Кроме того, как бы вы задокументировали эту функцию? Тип возвращаемого значения противоречив, если вы просто не скажете «он возвращает объект», что является слишком общим, чтобы быть полезным.

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

Я протестировал, и это работает, но, например, в c такие вещи не работают.

Он также работает на C (и, вероятно, на любом языке), хотя и немного по-другому. В конце концов, то, что возвращает Python, является «объектом», это зависит от вызывающего, чтобы интерпретировать это. Это очень похоже на возврат void* из функции C и принудительное выполнение вызывающим объектом соответствующего приведения. Что также является очень плохой практикой. Еще хуже в C , так как он строго типизирован.

Вы не знаете, есть ли какое-нибудь правило на этот счет, например, PEP8 или что-то в этом роде ?

Ну, не прямо об этой ситуации, но в «Руководстве по стилю PEP 8 для кода Python» говорится «Будьте последовательны в операторах возврата»..