Канонический способ вернуть несколько значений в языках, которые его поддерживают, часто кортеж.
Вариант: использование кортежа
Рассмотрим этот тривиальный пример:
def f(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return (y0, y1, y2)
Однако это быстро становится проблематичным, так как количество возвращаемых значений увеличивается. Что если вы хотите вернуть четыре или пять значений? Конечно, вы можете продолжать их использовать, но легко забыть, какое значение где. Также довольно уродливо распаковывать их там, где вы хотите их получить.
Вариант: использование словаря
Следующий логический шаг, по-видимому, состоит в том, чтобы ввести своего рода "запись записи". В Python очевидный способ сделать это с помощью dict
.
Учтите следующее:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0': y0, 'y1': y1 ,'y2': y2}
(Для ясности, y0, y1 и y2 означают только абстрактные идентификаторы. Как уже отмечалось, на практике вы будете использовать значимые идентификаторы.)
Теперь у нас есть механизм, с помощью которого мы можем проецировать конкретного члена возвращаемого объекта. Например,
result['y0']
Вариант: использование класса
Однако есть и другой вариант. Вместо этого мы могли бы вернуть специализированную структуру. Я описал это в контексте Python, но я уверен, что это применимо и к другим языкам. Действительно, если вы работали в C, это вполне может быть вашим единственным вариантом. Здесь идет:
class ReturnValue:
def __init__(self, y0, y1, y2):
self.y0 = y0
self.y1 = y1
self.y2 = y2
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return ReturnValue(y0, y1, y2)
В Python два предыдущих, возможно, очень похожи с точки зрения сантехники - в конце концов, { y0, y1, y2 }
в конечном итоге просто становится записями во внутреннем __dict__
ReturnValue
.
Python предоставляет еще одну дополнительную функцию для крошечных объектов - атрибут __slots__
. Класс может быть выражен как:
class ReturnValue(object):
__slots__ = ["y0", "y1", "y2"]
def __init__(self, y0, y1, y2):
self.y0 = y0
self.y1 = y1
self.y2 = y2
Из Справочного руководства по Python:
Объявление
__slots__
принимает последовательность переменных экземпляра и резервирует достаточно места в каждом экземпляре для хранения значения для каждой переменной. Пространство сэкономлено, поскольку__dict__
создается не для каждого экземпляра.
Вариант: использование класса данных (Python 3. 7+)
Используя новые классы данных Python 3.7, верните класс с автоматически добавленными специальными методами, набором текста и другими полезными инструментами:
@dataclass
class Returnvalue:
y0: int
y1: float
y3: int
def total_cost(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return ReturnValue(y0, y1, y2)
Вариант: использование списка
Еще одно предложение, которое я упустил, исходит от Билла Ящерицы:
def h(x):
result = [x + 1]
result.append(x * 3)
result.append(y0 ** y3)
return result
Это мой наименее любимый метод, хотя. Я предполагаю, что я испорчен воздействием Haskell, но идея списков смешанного типа всегда чувствовала себя неудобно для меня. В этом конкретном примере список имеет тип -не-смешанный, но, возможно, это может быть.
Насколько я могу судить, используемый таким образом список действительно ничего не дает в отношении кортежа. Единственная реальная разница между списками и кортежами в Python заключается в том, что списки являются изменяемыми, а кортежи - нет.
Лично я склонен переносить условные обозначения из функционального программирования: использовать списки для любого количества элементов одного типа и кортежи для фиксированного числа элементов заранее определенных типов.
Вопрос
После длинной преамбулы встает неизбежный вопрос. Какой метод (как вы думаете) лучше?
Я обычно ловил себя на пути к словарю, потому что он требует меньше работы по настройке. Однако, с точки зрения типов, вам лучше пойти по пути класса, поскольку это поможет вам избежать путаницы в том, что представляет собой словарь.
С другой стороны, есть некоторые в сообществе Python, которые считают, что подразумеваемые интерфейсы должны быть предпочтительнее явных интерфейсов, и в этот момент тип объекта действительно не имеет значения, поскольку вы в основном полагаетесь на соглашение о том, что один и тот же атрибут всегда будет иметь одинаковое значение.
Итак, как вы можете вернуть несколько значений в Python?