Como você retornar vários valores em Python?

votos
768

A forma canônica para retornar vários valores em linguagens que suportam muitas vezes é tupling .

Opção: Usando uma tupla

Considere este exemplo trivial:

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0,y1,y2)

No entanto, este rapidamente se torna problemático como o número de valores retornados aumenta. E se você quiser voltar quatro ou cinco valores? Claro, você poderia manter tupling-los, mas fica fácil esquecer que o valor é onde. É também bastante feio para descompactá-los onde quer que você deseja recebê-los.

Opção: Usando um dicionário

O próximo passo lógico parece ser a de introduzir algum tipo de 'notação record'. Em python, a maneira mais óbvia de fazer isso é por meio de um dict.

Considere o seguinte:

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

(Edit- Só para ficar claro, y0, y1 e y2 são voltadas apenas como identificadores abstratos. Como apontado, na prática, você pode usar identificadores significativos)

Agora, nós temos um mecanismo pelo qual podemos projetar um determinado membro do objeto retornado. Por exemplo,

result['y0']

Opção: Usando uma classe

No entanto, não há outra opção. Poderíamos, em vez retornar de uma estrutura especializada. Eu enquadrado isso no contexto de Python, mas eu tenho certeza que ele se aplica a outros idiomas. De fato, se você estava trabalhando em C esta pode muito bem ser a sua única opção. Aqui vai:

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)

Em python os dois anteriores são, talvez, muito semelhantes em termos de plumbing- Afinal { y0, y1, y2 }acabar sendo entradas na interna __dict__do ReturnValue.

Há uma característica adicional fornecida pelo Python embora para objetos minúsculos, o __slots__atributo. A classe pode ser expressa como:

class ReturnValue(object):
  __slots__ = [y0, y1, y2]
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

A partir do Python Reference Manual :

A __slots__declaração leva uma seqüência de variáveis de instância e as reservas apenas espaço suficiente em cada instância para manter um valor para cada variável. O espaço é salvo, porque __dict__não é criada para cada instância.

Opção: Usando uma lista

Outra sugestão que eu tinha esquecido vem de Bill o lagarto:

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

Este é o meu método menos favorito embora. Acho que eu estou contaminado por exposição a Haskell, mas a idéia de listas do tipo mista sempre se sentiu desconfortável para mim. Neste exemplo em particular a lista é de tipo misto -not-, mas concebivelmente poderia ser. Uma lista usado desta forma realmente não ganha nada com relação à tupla, tanto quanto eu posso dizer. A única diferença real entre listas e tuplas em Python é que as listas são mutáveis , wheras tuplas não são. Eu, pessoalmente, tendem a carregar sobre as convenções de programação funcional: listas de uso para qualquer número de elementos do mesmo tipo, e tuplas para um número fixo de elementos de tipos predeterminados.

Questão

Após o longo preâmbulo, vem a pergunta inevitável. Qual método (você acha) é o melhor?

Eu normalmente me vi indo a rota dicionário porque envolve menos trabalho set-up. De uma perspectiva de tipos no entanto, você pode ser melhor fora de ir a rota classe, uma vez que podem ajudá-lo a evitar confundir o que é um dicionário representa. Por outro lado, existem alguns na comunidade Python que sentem as interfaces implícitas deve ser preferido a interfaces explícitas , altura em que o tipo do objeto realmente não é relevante, uma vez que você está confiando basicamente na convenção que o mesmo atributo sempre têm o mesmo significado.

Então, como fazer -que você retornar vários valores em Python?

Publicado 10/12/2008 em 02:55
fonte usuário
Em outras línguas...                            


14 respostas

votos
481

Tuplas nomeadas foram adicionados em 2,6 para esta finalidade. Veja também os.stat para um exemplo builtin similar.

>>> 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
Respondeu 10/12/2008 em 17:36
fonte usuário

votos
156

Para pequenos projetos Acho que é mais fácil de trabalhar com tuplas. Quando isso fica muito difícil de gerir (e não antes) eu começar a agrupar as coisas em estruturas lógicas, no entanto eu acho que seu uso sugerido de dicionários e objetos ReturnValue é errado (ou muito simplista).

Retornando um dicionário com chaves y0, y1, y2 etc não oferece nenhuma vantagem sobre tuplas. Retornando uma instância ReturnValue com propriedades .y0 .y1 .y2 etc não oferece nenhuma vantagem sobre tuplas também. Você precisa começar a nomear as coisas, se você quiser chegar a algum lugar, e você pode fazer isso usando tuplas de qualquer maneira:

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

IMHO, a única boa técnica para além tuplas é retornar objetos reais com métodos e propriedades adequadas, como você começa a partir re.match()ou open(file).

Respondeu 10/12/2008 em 03:22
fonte usuário

votos
101

Muitas das respostas sugerem que você precisa para retornar uma coleção de algum tipo, como um dicionário ou uma lista. Você poderia deixar de fora a sintaxe extra e basta escrever os valores de retorno, separados por vírgulas. Nota: esta técnica retorna uma tupla.

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

dá:

True
False
Respondeu 14/04/2016 em 20:08
fonte usuário

votos
49

Eu voto para o dicionário.

Eu acho que se eu fazer uma função que retorna nada mais do que 2-3 variáveis ​​Vou dobrá-los em um dicionário. Caso contrário, eu tendem a esquecer a ordem eo conteúdo do que eu estou voltando.

Além disso, a introdução de uma estrutura de 'especial' faz seu código mais difícil de seguir. (Alguém vai ter que procurar através do código para descobrir o que é)

Se a sua preocupação sobre o tipo de olhar para cima, use chaves de dicionário descritivos, por exemplo, 'x-valores da lista'.

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }
Respondeu 10/12/2008 em 03:42
fonte usuário

votos
26

Outra opção seria usar geradores:

>>> 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

Embora tuplas IMHO são geralmente melhor, exceto nos casos em que os valores que estão sendo devolvidos são candidatos para encapsulamento em uma classe.

Respondeu 23/02/2014 em 16:26
fonte usuário

votos
20
>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3
Respondeu 21/01/2015 em 20:57
fonte usuário

votos
19

Eu prefiro usar tuplas sempre que uma tupla se sente "natural"; coordenadas são um exemplo típico, em que os objectos separados podem estar na sua própria, por exemplo, em um eixo cálculos apenas dimensionamento, e a ordem é importante. Nota: se eu posso classificar ou embaralhar os itens sem um efeito adverso sobre o significado do grupo, então eu provavelmente não deveria usar uma tupla.

Eu uso dicionários como um valor de retorno somente quando os objetos agrupados nem sempre são os mesmos. Pense cabeçalhos de e-mail opcionais.

Para o resto dos casos, onde os objetos agrupados têm significado inerente dentro do grupo ou um objeto de pleno direito com seus próprios métodos é necessário, eu uso uma classe.

Respondeu 10/12/2008 em 03:40
fonte usuário

votos
17

eu prefiro

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

parece que tudo o resto é apenas código extra para fazer a mesma coisa.

Respondeu 10/12/2008 em 03:00
fonte usuário

votos
11

Tuplas, dicts e objetos do Python oferecer o programador uma troca suave entre formalidade e conveniência para estruturas de dados pequenos ( "coisas"). Para mim, a escolha de como representar uma coisa é ditada principalmente pela forma como eu vou usar a estrutura. Em C ++, é uma convenção comum o uso structde dados somente itens e classpara objetos com métodos, mesmo que você pode colocar legalmente métodos em um struct; meu hábito é semelhante em Python, com dicte tupleno lugar de struct.

Para coordenar conjuntos, vou usar um tupleem vez de um ponto classou de um dict(e note que você pode usar um tuplecomo uma chave de dicionário, por isso dicts fazer grandes arrays multidimensionais esparsos).

Se eu vou ser a iteração sobre uma lista de coisas, eu prefiro desempacotamento tuples sobre a iteração:

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

... como a versão de objeto é mais confuso para ler:

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

... e muito menos o dict.

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

Se a coisa é amplamente utilizado, e você encontrar-se fazendo operações não-triviais semelhantes sobre ele em vários locais no código, então é geralmente útil para torná-lo um objeto de classe com métodos apropriados.

Finalmente, se eu vou ser a troca de dados com os componentes do sistema não-Python, eu vou na maioria das vezes mantê-los em um dict, porque isso é o mais adequado para serialização JSON.

Respondeu 13/08/2013 em 21:18
fonte usuário

votos
11

+1 na sugestão de uma classe recipiente chamado de S. Lott.

Para Python 2.6 e acima, um tuple chamado fornece uma maneira útil de criar facilmente essas classes de contêiner, e os resultados são "leves e não requerem mais memória do que tuplas regulares".

Respondeu 10/12/2008 em 04:51
fonte usuário

votos
11

Geralmente, a "estrutura especializada" na verdade é um estado atual sensata de um objeto, com seus próprios métodos.

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

Eu gostaria de encontrar nomes para estruturas anônimos sempre que possível. nomes significativos tornar as coisas mais claras.

Respondeu 10/12/2008 em 03:15
fonte usuário

votos
2

Em linguagens como Python, eu costumo usar um dicionário, pois envolve menos sobrecarga do que criar uma nova classe.

No entanto, se eu me encontrar constantemente retornando o mesmo conjunto de variáveis, em seguida, que provavelmente envolve uma nova classe que eu vou levar para fora.

Respondeu 10/12/2008 em 03:02
fonte usuário

votos
0

"Melhor" é uma decisão parcialmente subjetivo. Use tuplas para pequenos conjuntos de retorno no caso geral onde um imutável é aceitável. Uma tupla é sempre preferível a uma lista quando mutabilidade não é um requisito.

Para mais valores de retorno complexos, ou para o caso em que a formalidade é valioso (ou seja, alta código de valor) uma tupla nomeado é melhor. Para o caso mais complexo um objeto é geralmente melhor. No entanto, é realmente a situação que importa. Se faz sentido para retornar um objeto, porque isso é o que você têm, naturalmente, no final da função (por exemplo, padrão de fábrica), em seguida, retornar o objeto.

Como o homem sábio disse:

otimização prematura é a raiz de todo o mal (ou pelo menos a maior parte dele) na programação.

Respondeu 29/08/2018 em 19:30
fonte usuário

votos
0

Gostaria de usar um dicionário para passar e retornar valores de uma função:

Use forma variável, tal como definido no formulário .

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}

Esta é a maneira mais eficiente para mim e para a unidade de processamento.

Você tem que passar apenas um ponteiro e devolver apenas um ponteiro para fora.

Você não tem que mudar funções (milhares deles) argumentos sempre que você fizer uma alteração em seu código.

Respondeu 30/10/2017 em 14:01
fonte usuário

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