在支持它的语言中返回多个值的规范方法通常是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(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)
在 python 中,前两个在管道方面可能非常相似 - 毕竟{ y0, y1, y2 }
最终只是ReturnValue
内部__dict__
中的条目。
虽然对于微小的对象__slots__
属性,但 Python 提供了一个附加功能。该课程可表示为:
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__
。
我忽略的另一个建议来自比尔蜥蜴:
def h(x):
result = [x + 1]
result.append(x * 3)
result.append(y0 ** y3)
return result
这是我最不喜欢的方法。我想我已经被 Haskell 暴露了,但混合型列表的想法一直让我觉得不舒服。在这个特定的例子中,列表是 - not - 混合类型,但可以想象它可以。就我所知,以这种方式使用的列表实际上并没有获得关于元组的任何信息。 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
允许您使用文档字符串,默认值和类型注释。
示例(来自文档):
class Employee(NamedTuple): # inherit from collections.NamedTuple
name: str
id: int = 3 # default value
employee = Employee('Guido')
assert employee.id == 3
对于小型项目,我发现使用元组最容易。当它变得难以管理(而不是之前)时,我开始将事物分组到逻辑结构中,但是我认为你建议使用字典和 ReturnValue 对象是错误的(或过于简单化)。
返回带有键 y0,y1,y2 等的字典并不比元组提供任何优势。返回具有属性. y0 .y1 .y2 等的 ReturnValue 实例也不会提供任何优于元组的优势。如果你想要到达任何地方,你需要开始命名,你可以使用元组来做到这一点:
def getImageData(filename):
[snip]
return size, (format, version, compression), (width,height)
size, type, dimensions = getImageData(x)
恕我直言,超越元组的唯一好方法是返回具有适当方法和属性的真实对象,就像从re.match()
或open(file)
。
很多答案都表明你需要返回某种类型的集合,比如字典或列表。你可以省去额外的语法,然后写出以逗号分隔的返回值。注意:这在技术上会返回一个元组。
def f():
return True, False
x, y = f()
print(x)
print(y)
得到:
True
False