Как вы вернуть несколько значений в Python?

голоса
768

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

Вариант: Используя кортеж

Рассмотрим этот тривиальный пример:

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 }

(Edit- Просто чтобы быть ясно, y0, y1 и y2 предназначены только как абстрактные идентификаторы. Как было отмечено, на практике нужно использовать осмысленные идентификаторы)

Теперь у нас есть механизм, посредством которого мы можем проецировать из конкретного элемента возвращаемого объекта. Например,

result['y0']

Вариант: Использование класса

Тем не менее, есть еще один вариант. Мы могли бы вместо того, чтобы вернуть специализированную структуру. Я обрамление это в контексте Python, но я уверен, что это относится и к другим языкам, а также. В самом деле, если бы вы работали в C это вполне может быть единственным вариантом. Вот оно:

class ReturnValue(object):
  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)

В питона предыдущие два, возможно , очень похожи с точки зрения plumbing- Ведь { 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 Reference Manual :

__slots__Декларация принимает последовательность переменных экземпляра и резервов только достаточно места в каждом отдельном случае для хранения значения для каждой переменной. Пространство сохраняется , потому что __dict__не создается для каждого экземпляра.

Вариант: Использование списка

Еще одно предложение, которое я упускать из виду приходит от Билла ящерица:

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

Это мой самый нелюбимый метод , хотя. Я полагаю , я испорчена под воздействием Haskell, но идея списки смешанного типа всегда чувствовал себя неудобно для меня. В этом конкретном примере список -not- смешанного типа, но теоретически может быть. Список используется таким образом , на самом деле не получает ничего по отношению к кортежу, насколько я могу судить. Единственное реальное различие между списками и кортежей в Python является то , что списки являются изменяемыми , кортежи не Тогда как есть. Я лично , как правило, переносятся на условности из функционального программирования: списки использования для любого числа элементов одного и того же типа, и кортежей для фиксированного числа элементов заданных типов.

Вопрос

После долгой преамбулы, наступает неизбежный вопрос. Какой метод (как вы думаете) лучше всего?

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

Итак, как же -you- возвращать несколько значений в Python?

Задан 10/12/2008 в 02:55
источник пользователем
На других языках...                            


14 ответов

голоса
481

Именованные кортежи были добавлены в версии 2.6 для этой цели. Смотрите также os.stat для подобного встроено примера.

>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2
Ответил 10/12/2008 в 17:36
источник пользователем

голоса
156

Для небольших проектов, я считаю, что проще работать с кортежами. Когда это становится слишком трудно управлять (и не раньше) я начинаю группирование вещи в логические структуры, однако я думаю, что ваше Рекомендованное использование словарей и объектов ReturnValue неправильно (или слишком упрощенно).

Возвращаясь словарем с ключами y0, y1, y2 и т.д. не дают никаких преимуществ перед кортежами. Возвращение ReturnValue экземпляра со свойствами .y0 .y1 .y2 и т.д. не дает никаких преимуществ по сравнению кортежей либо. Вы должны начать называть вещи, если вы хотите получить в любом месте, и вы можете сделать это с помощью кортежей в любом случае:

def getImageData(filename):
  [snip]
  return size, (format, version, compression), (width,height)
size, type, dimensions = getImageData(x)

ИМХО, единственная хорошая техника за кортежи , чтобы вернуть реальные объекты с соответствующими методами и свойствами, как вы получите от re.match()или open(file).

Ответил 10/12/2008 в 03:22
источник пользователем

голоса
101

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

def f():
    return True, False
x, y = f()
print(x)
print(y)

дает:

True
False
Ответил 14/04/2016 в 20:08
источник пользователем

голоса
49

Я голосую за словарем.

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

Кроме того, введение «специальная» структуры делает код более трудно следовать. (Кто-то будет искать через код, чтобы узнать, что это такое)

Если беспокоит тип просмотра, используйте описательные ключи словаря, например, «х-список значений».

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }
Ответил 10/12/2008 в 03:42
источник пользователем

голоса
26

Другим вариантом было бы с помощью генераторов:

>>> def f(x):
        y0 = x + 1
        yield y0
        yield x * 3
        yield y0 ** 4


>>> a, b, c = f(5)
>>> a
6
>>> b
15
>>> c
1296

Хотя ИМХО кортежи, как правило, лучше всего, за исключением случаев, когда значения возвращаются являются кандидатами для инкапсуляции в классе.

Ответил 23/02/2014 в 16:26
источник пользователем

голоса
20
>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3
Ответил 21/01/2015 в 20:57
источник пользователем

голоса
19

Я предпочитаю использовать кортежи, когда кортеж чувствует «естественным»; координаты являются типичным примером, где отдельные объекты могут стоять на своем собственном, например, в одной оси только масштабирование вычислений и порядок важен. Примечание: если я могу сортировать или перетасовать элементы без отрицательного влияния на смысл группы, то я, вероятно, не следует использовать кортеж.

Я использую словари в качестве возвращаемого значения только тогда, когда Сгруппированные объекты не всегда одинаковы. Подумайте дополнительные заголовки сообщений электронной почты.

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

Ответил 10/12/2008 в 03:40
источник пользователем

голоса
17

я предпочитаю

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

кажется, все остальное просто дополнительный код, чтобы сделать то же самое.

Ответил 10/12/2008 в 03:00
источник пользователем

голоса
11

Кортежи языка Python, dicts и объекты предлагают программист гладкую компромисс между формальностью и удобством для небольших структур данных ( «вещей»). Для меня выбор , как представить вещь диктуется в основном , как я буду использовать структуру. В C ++, это общее соглашение , чтобы использовать structдля передачи данных только для элементов и classобъектов с помощью методов, даже если вы можете законно ставить методы на struct; моя привычка похожа на Python, с dictи tupleна месте struct.

Для координат наборов, я буду использовать tupleвместо точки classили dict(и обратите внимание , что вы можете использовать в tupleкачестве словаря ключа, так что dicts делают большие разреженные многомерные массивы).

Если я собираюсь быть итерация списка вещей, я предпочитаю распаковку tupleсекунду на итерации:

for score,id,name in scoreAllTheThings():
    if score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(score,id,name)

... как версия объекта более суматоха следующим образом:

for entry in scoreAllTheThings():
    if entry.score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)

... не говоря уже о dict.

for entry in scoreAllTheThings():
    if entry['score'] > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])

Если вещь широко используется, и вы сами делаете подобные нетривиальные операции на нем в нескольких местах в коде, то это, как правило, стоит сделать это класс объектов с соответствующими методами.

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

Ответил 13/08/2013 в 21:18
источник пользователем

голоса
11

+1 по предложению С. Лотт в поименованном классе контейнера.

Для Python 2.6 и выше, имя Кортеж обеспечивает полезный способ легко создавать эти классы контейнеров, и результаты «легкие и не требуют больше памяти , чем обычные кортежи».

Ответил 10/12/2008 в 04:51
источник пользователем

голоса
11

Как правило, «специализированная структура» на самом деле является разумным состоянием тока объекта, со своими собственными методами.

class Some3SpaceThing(object):
  def __init__(self,x):
    self.g(x)
  def g(self,x):
    self.y0 = x + 1
    self.y1 = x * 3
    self.y2 = y0 ** y3

r = Some3SpaceThing( x )
r.y0
r.y1
r.y2

Я хотел бы найти имена для анонимных структур, где это возможно. Значимые имена делают вещи более ясно.

Ответил 10/12/2008 в 03:15
источник пользователем

голоса
2

В таких языках, как Python, я обычно использую словарь, как это требует меньше накладных расходов, чем создание нового класса.

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

Ответил 10/12/2008 в 03:02
источник пользователем

голоса
0

«Бест» является частично субъективным решением. Используйте кортежи для небольших наборов возврата в общем случае, когда непреложным является приемлемым. Кортеж всегда предпочтительнее список, когда Изменчивость не является обязательным требованием.

Для более сложных возвращаемых значений, или для случая, когда формальность является ценной (т.е. высокого кода значения) именованный кортеж лучше. Для наиболее сложном случае объект, как правило, лучше. Тем не менее, это действительно ситуация, которая имеет значение. Если это имеет смысл, чтобы вернуть объект, потому что это то, что вы, естественно, в конце функции (например, завод шаблон), а затем возвращает объект.

Как мудрый человек сказал:

Преждевременная оптимизация является корнем всего зла (или по крайней мере большинство из них) в программировании.

Ответил 29/08/2018 в 19:30
источник пользователем

голоса
0

Я хотел бы использовать Dict, чтобы передавать и возвращать значения из функции:

Используйте переменную форму , как определено в форме .

form = {
    'level': 0,
    'points': 0,
    'game': {
        'name': ''
    }
}


def test(form):
    form['game']['name'] = 'My game!'
    form['level'] = 2

    return form

>>> print(test(form))
{u'game': {u'name': u'My game!'}, u'points': 0, u'level': 2}

Это самый эффективный способ для меня, и для блока обработки.

Вы должны пройти только один указатель в и возвращать только один указатель из.

Вы не должны изменять функции (тысячи из них) аргументов, когда вы вносите изменения в коде.

Ответил 30/10/2017 в 14:01
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more