Канонический способ вернуть несколько значений в языках, которые его поддерживают, часто кортежем .
Вариант: Использование кортежа
Рассмотрим этот тривиальный пример:
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 заключается в том, что списки являются изменяемыми , а кортежи - нет.
Лично я склонен переносить условные обозначения из функционального программирования: использовать списки для любого количества элементов одного типа и кортежи для фиксированного числа элементов предопределенных типов.
Вопрос
После длинной преамбулы встает неизбежный вопрос. Какой метод (как вы думаете) лучше?
y3
, которое будет делать то же самое.
y3
, но если y3 не объявлен как глобальный, это может привести кNameError: global name 'y3' is not defined
простому использованию3
?