Какой стиль использовать для неиспользуемых возвращаемых параметров при вызове функции Python

30

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

a, b, c = foo()

и затем просто не используя bи cкакой из следующих вариантов должен быть предпочтительным (или есть другой?):

Вариант 1 (подчеркивание)

a, _, _ = foo()

(что очень ясно и просто, но может противоречить _ = gettext.gettextиспользуемому во многих приложениях, использующих перевод)

Вариант 2 (фиктивное имя)

a, unused, unused = foo()

(я думаю, это не очень привлекательно для других имен dummy)

Вариант 3 (индекс)

a = foo()[0]

(для меня ()[0]внешность не питонна ...)

user49643
источник
В отличие от моего предыдущего ответа, что происходит с вариантом 3, когда вы теперь хотите сослаться на 2/3 возвращаемых значений? Я удалил свой ответ, потому что понял, что недостаточно знаю Python, чтобы оправдать его.
Крейг,
@Craige: я не видел ваш ответ, прежде чем вы удалили его ... Вы спрашиваете, a, b = foo()[0:2]будет ли работать? Если да: да, это так :)
user49643
Это было именно то, что я спрашивал. Если это сработает (как вы и сказали), я бы воспользовался вариантом 3. Я повторяю свой ответ, учитывая эту информацию.
Крейг,
3
Кстати, в наши дни вы можете использовать довольно красивый синтаксис для «расширенной» распаковки : a, *_ = foo()отбросит все значения, кроме первого.
Бенджамин Ходжсон
дубликат stackoverflow.com/questions/431866/…
Тревор Бойд Смит

Ответы:

17

Использование подчеркивания для неиспользуемых переменных определенно допустимо. Однако следует помнить, что в некоторых кодовых базах это не вариант, так как этот идентификатор зарезервирован для сокращения gettext. Это наиболее частое возражение против этого стиля (хотя, насколько я могу судить, это не проблема для большинства). Я все еще рекомендую это, и всегда использую это непосредственно.

Имена нравятся dummyили unusedимеют тенденцию раздражать меня лично, и я не вижу их очень часто (в Python, то есть - я знаю кодовую базу Delphi, которая dummyсвободно используется , и она также просочилась в сценарии, связанные с рассматриваемой программой). Я бы посоветовал вам против этого.

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

  • Он не взорвется, если количество значений отличается от ожидаемого. Это может быть полезно для обнаружения ошибок и опечаток.
  • Это работает только тогда, когда возвращаемое значение является последовательностью (это в основном кортежи и списки, но давайте будем оставаться общими). Случайно, я знаю один класс в моем коде (двумерный вектор), который является итеративным и дает постоянное число значений (и, таким образом, может использоваться при распаковке назначений), но не индексируется.

источник
Я же на самом деле упомянуть GetText в моем вопросе :) Во всяком случае, я принял ваш ответ - это , кажется , что нет четкого единого общепринятого стиля , но ваш ответ поднял некоторые интересные моменты.
user49643
Чтобы избежать проблем с gettext (или избежать аналогичных проблем в интерпретаторе), вы можете использовать двойные подчеркивания (aka dunders) вместо одинарных.
Зим
21

Пилинт привел меня в привычку делать это так:

widget, _parent, _children = f()

То есть неиспользуемые результаты имеют описательное имя с префиксом _. Pylint рассматривает локальных пользователей с префиксом _ как неиспользуемые, а глобальные или атрибуты с префиксом _ как приватные.

Винсент Повирк
источник
3

Если во всем проекте вы делаете это только один раз или очень редко для конкретной функции, я бы использовал вариант 1, если вы знаете, gettextчто в этом модуле нет проблем, а в противном случае вариант 3.

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

bar = foo()

А потом работать с bar.a, bar.bи bar.c.

LVC
источник
2

Как уже говорили другие, подчеркивание ( _) является стандартом. Но если для перевода используется подчеркивание, я думаю, что двойное подчеркивание - лучшая альтернатива.

var, __, value = "VAR=value".partition('=')

Лучше, чем они:

var, unused, value = "VAR=value".partition('=')

var, unused_del, value = "VAR=value".partition('=')

var, _del, value = "VAR=value".partition('=')

Рик Мор
источник
1

Я не программист на Python, но для меня третий вариант имеет смысл.

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

Помимо ясности, почему вы хотите назначить неиспользуемые значения слоту в памяти (как в вариантах 1 и 2)? Это было бы плохим решением с точки зрения управления памятью.

Craige
источник
Если вы всегда помещаете его в одну и ту же переменную, не будет ли проблема с памятью только размером одной переменной в любой момент времени? Я не думаю, что это должно быть проблемой.
Майкл
-2

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

РЕДАКТИРОВАТЬ:

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

c0da
источник
-1 Полагаю, он не определил функцию и поэтому не может изменить то, что она возвращает. Я думаю, что он имеет в виду, когда функция возвращает 3 значения, но он заботится только о 1 в своем текущем сценарии использования.
Крейг
@Craige: Да, я имел в виду функции, которые я не мог изменить - я добавил примечание к своему вопросу, разъясняющее это.
user49643
Ты уверен насчет накладных расходов? Я попробовал очень простой тест ipython: %timeit a, _, _ = foo()против, %timeit a = foo()[0]который привел к 123 нс против 109 нс, то есть нарезка, кажется, на самом деле быстрее, чем распаковка значения. Или вы имели в виду какую-то конкретную ситуацию?
user49643
2
Другой вероятный сценарий - вам не нужны все значения для некоторых вызовов.
Кит Томпсон,