A maneira canônica de retornar vários valores em idiomas que o suportam é geralmente a tupla .
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, isso rapidamente se torna problemático à medida que o número de valores retornados aumenta. E se você quiser retornar quatro ou cinco valores? Claro, você pode continuar os tupleando, mas fica fácil esquecer qual é o valor. Também é muito feio desembalá-los onde você quiser recebê-los.
Opção: usando um dicionário
O próximo passo lógico parece ser a introdução de algum tipo de 'notação de registro'. No Python, a maneira óbvia de fazer isso é por meio de a dict
.
Considere o seguinte:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0': y0, 'y1': y1 ,'y2': y2}
(Só para esclarecer, y0, y1 e y2 são apenas identificadores abstratos. Como apontado, na prática, você usaria identificadores significativos.)
Agora, temos um mecanismo pelo qual podemos projetar um membro específico do objeto retornado. Por exemplo,
result['y0']
Opção: usando uma classe
No entanto, há outra opção. Em vez disso, poderíamos retornar uma estrutura especializada. Enquadrei isso no contexto do Python, mas tenho certeza que se aplica a outras linguagens também. De fato, se você estivesse trabalhando em C, essa poderia ser sua única opção. Aqui vai:
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)
No Python, os dois anteriores são talvez muito semelhantes em termos de encanamento - afinal { y0, y1, y2 }
acabam sendo entradas no interior __dict__
do ReturnValue
.
Há um recurso adicional fornecido pelo Python 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
No manual de referência do Python :
A
__slots__
declaração utiliza uma sequência de variáveis de instância e reserva espaço suficiente em cada instância para armazenar um valor para cada variável. O espaço é salvo porque__dict__
não é criado para cada instância.
Opção: usando uma classe de dados (Python 3.7+)
Usando as novas classes de dados do Python 3.7, retorne uma classe com métodos especiais adicionados automaticamente, digitação e outras ferramentas úteis:
@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)
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, no entanto. Suponho que estou contaminado pela exposição a Haskell, mas a ideia de listas de tipos mistos sempre me pareceu desconfortável. Neste exemplo em particular, a lista não é do tipo misto, mas pode ser concebível.
Uma lista usada dessa maneira realmente não ganha nada em relação à tupla, até onde eu sei. A única diferença real entre listas e tuplas no Python é que as listas são mutáveis , enquanto as tuplas não são.
Pessoalmente, tenho a tendência de manter as convenções da programação funcional: use listas 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 preâmbulo longo, vem a pergunta inevitável. Qual método (você acha) é o melhor?
y3
, que também fará o mesmo trabalho.
y3
, mas, a menos que y3 seja declarado global, isso renderiaNameError: global name 'y3' is not defined
talvez apenas usar3
?