La forma canónica de devolver múltiples valores en idiomas que lo admiten es a menudo tupling .
Opción: usar una tupla
Considere este ejemplo trivial:
def f(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return (y0, y1, y2)
Sin embargo, esto rápidamente se vuelve problemático a medida que aumenta el número de valores devueltos. ¿Qué pasa si desea devolver cuatro o cinco valores? Claro, podrías seguir tuplándolos, pero es fácil olvidar qué valor es dónde. También es bastante feo desempacarlos donde quieras recibirlos.
Opción: usar un diccionario
El siguiente paso lógico parece ser introducir algún tipo de 'notación de registro'. En Python, la forma obvia de hacer esto es mediante a dict
.
Considera lo siguiente:
def g(x):
y0 = x + 1
y1 = x * 3
y2 = y0 ** y3
return {'y0': y0, 'y1': y1 ,'y2': y2}
(Para ser claros, y0, y1 e y2 son solo identificadores abstractos. Como se señaló, en la práctica se usarían identificadores significativos).
Ahora, tenemos un mecanismo por el cual podemos proyectar un miembro particular del objeto devuelto. Por ejemplo,
result['y0']
Opción: usar una clase
Sin embargo, hay otra opción. En cambio, podríamos devolver una estructura especializada. Lo he enmarcado en el contexto de Python, pero estoy seguro de que también se aplica a otros lenguajes. De hecho, si estuvieras trabajando en C, esta podría ser tu única opción. Aquí va:
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)
En Python, los dos anteriores son quizás muy similares en términos de fontanería, después de todo, { y0, y1, y2 }
terminan siendo entradas en el interior __dict__
del ReturnValue
.
Python proporciona una característica adicional para objetos pequeños, el __slots__
atributo. La clase podría expresarse como:
class ReturnValue(object):
__slots__ = ["y0", "y1", "y2"]
def __init__(self, y0, y1, y2):
self.y0 = y0
self.y1 = y1
self.y2 = y2
Del Manual de referencia de Python :
La
__slots__
declaración toma una secuencia de variables de instancia y reserva suficiente espacio en cada instancia para mantener un valor para cada variable. Se ahorra espacio porque__dict__
no se crea para cada instancia.
Opción: Usar una clase de datos (Python 3.7+)
Usando las nuevas clases de datos de Python 3.7, devuelve una clase con métodos especiales, mecanografía y otras herramientas útiles agregadas automáticamente:
@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)
Opción: usar una lista
Otra sugerencia que había pasado por alto proviene de Bill el Lagarto:
def h(x):
result = [x + 1]
result.append(x * 3)
result.append(y0 ** y3)
return result
Sin embargo, este es mi método menos favorito. Supongo que estoy contaminado por la exposición a Haskell, pero la idea de listas de tipos mixtos siempre me ha resultado incómoda. En este ejemplo particular, la lista es de tipo no mixto, pero posiblemente podría serlo.
Una lista utilizada de esta manera realmente no gana nada con respecto a la tupla por lo que puedo decir. La única diferencia real entre listas y tuplas en Python es que las listas son mutables , mientras que las tuplas no lo son.
Personalmente, tiendo a transferir las convenciones de la programación funcional: use listas para cualquier número de elementos del mismo tipo y tuplas para un número fijo de elementos de tipos predeterminados.
Pregunta
Después del largo preámbulo, viene la inevitable pregunta. ¿Qué método (crees) es el mejor?
y3
, que también hará el mismo trabajo.
y3
, pero a menos que y3 se declare global, ¿esto produciríaNameError: global name 'y3' is not defined
tal vez solo uso3
?