选项:使用元组
考虑下面这个简单的例子:
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 }最终只是在内部__dict__的入口ReturnValue。
Python提供了一项附加功能,尽管对于微小的对象,__slots__属性。该类可以表示为:
__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__未为每个实例创建空间,所以节省了空间。
__dict__
选项:使用数据类(Python 3.7+)
使用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中列表和元组之间的唯一真正区别是列表是可变的,而元组则不是。
我个人倾向于继承函数式编程的约定:对任何数量的相同类型的元素使用列表,对固定数量的预定类型的元素使用元组。
题
在冗长的序言之后,出现了不可避免的问题。你认为哪种方法最好?
我通常发现自己走了字典路线,因为它涉及的设置工作较少。但是,从类型的角度来看,你最好走类路线,因为这可以帮助你避免混淆字典所代表的含义。
另一方面,在Python社区中有些人认为隐式接口应该比显式接口更可取,这时对象的类型实际上是不相关的,因为你基本上是依赖于相同属性的约定将始终具有相同的含义。
那么,如何在Python中返回多个值?
为此,在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
在最新版本的Python 3(我认为是3.6+)中,新typing库提供了NamedTuple使命名元组更易于创建和更强大的类。通过继承,typing.NamedTuple你可以使用文档字符串,默认值和类型注释。
NamedTuple
typing.NamedTuple
示例(来自文档):
class Employee(NamedTuple): # inherit from typing.NamedTuple name: str id: int = 3 # default value employee = Employee('Guido') assert employee.id == 3