在支持多值的语言中,返回多值的典型方式通常是tupling。
考虑一下这个微不足道的例子。
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 }
最终只是作为ReturnValue'的内部
dict'中的条目。
不过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'的新数据类,返回一个带有自动添加的特殊方法、类型和其他有用工具的类。
@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 中,列表和元组之间唯一真正的区别是,列表是 可变的 ,而元组不是。
我个人倾向于继承函数式编程的惯例:用 list 来表示相同类型的任何数量的元素,用 tuples 来表示预定类型的固定数量的元素。
在冗长的序言之后,是一个不可避免的问题。哪种方法(你认为)是最好的?
我通常发现自己走的是字典路线,因为它涉及较少的设置工作。然而,从类型的角度来看,你最好还是走类的路线,因为这可以帮助你避免混淆字典的含义。
另一方面,Python 社区中的一些人认为 隐含接口应该比显式接口更受欢迎,在这一点上,对象的类型真的不重要,因为你基本上依赖于同一属性总是具有相同含义的约定。
那么,你如何在 Python 中返回多个值呢?
一般来说,"专门结构"实际上是一个对象的合理的当前状态,有它自己的方法。
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
我喜欢在可能的情况下为匿名结构找到名字。 有意义的名字会让事情更清晰。