你如何返回Python中的多个值?
在支持它的语言中返回多个值的规范方法通常是晦涩难懂的 。
选项:使用元组
考虑这个微不足道的例子:
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__
中的条目。
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__
不是为每个实例创建的。
选项:使用列表
我忽略的另一个建议来自比尔蜥蜴:
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) >>> px, py 1 2 >>> p[0], p[1] 1 2
对于小型项目,我发现使用元组最容易。 当它变得太难以管理(而不是之前),我开始分组的东西到逻辑结构,但是我认为你建议使用的字典和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
我投票给字典。
我发现如果我做了一个返回超过2-3个变量的函数,我会把它们放在一个字典中。 否则,我往往忘记我回来的顺序和内容。
而且,引入“特殊”结构使得你的代码更难以遵循。 (其他人将不得不搜索代码来找出它是什么)
如果您关心类型查询,请使用描述性字典键,例如“x值列表”。
def g(x): y0 = x + 1 y1 = x * 3 y2 = y0 ** y3 return {'y0':y0, 'y1':y1 ,'y2':y2 }
另一种选择是使用发电机:
>>> def f(x): y0 = x + 1 yield y0 yield x * 3 yield y0 ** 4 >>> a, b, c = f(5) >>> a 6 >>> b 15 >>> c 1296
虽然恕我直言,元组通常是最好的,除非返回值是封装在类中的候选者。
>>> def func(): ... return [1,2,3] ... >>> a,b,c = func() >>> a 1 >>> b 2 >>> c 3
每当一个元组感觉“自然”时,我更喜欢使用元组; 坐标是一个典型的例子,其中独立的对象可以自己站立,例如单轴缩放计算。
只有当分组的对象不总是相同时,我才使用字典作为返回值。 想想可选的邮件标题。
对于其他情况,组合对象在组内具有固有含义,或者需要具有自己方法的完全对象,我使用一个类。
我更喜欢
def g(x): y0 = x + 1 y1 = x * 3 y2 = y0 ** y3 return {'y0':y0, 'y1':y1 ,'y2':y2 }
似乎一切都只是额外的代码来做同样的事情。
S.Lott建议使用命名的容器类+1。
对于python 2.6或更高版本, 命名元组提供了一种轻松创建这些容器类的有用方法,并且结果是“轻量级且不需要比常规元组更多的内存”。
一般来说,“专业化的结构”实际上是一个对象的一个合理的现状,用它自己的方法。
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
我喜欢在可能的地方找到匿名结构的名称。 有意义的名字使事情变得更加清晰。
Python的元组,字典和对象为程序员提供了小数据结构(“事物”)的形式化和便利性之间的平滑权衡。 对我而言,如何表现一件事物的选择主要由我将如何使用该结构决定。 在C ++中,尽管可以合法地将方法放在struct
上,但是将struct
用于仅含数据的项目和具有方法的对象的方法是常见的约定; 我的习惯在Python中是类似的,用dict
和tuple
代替struct
。
对于坐标集,我将使用一个tuple
而不是一个点class
或dict
(并注意,您可以使用一个tuple
作为字典键,所以dict
使稀疏的多维数组)。
如果我要迭代一系列事物,我更喜欢在迭代中解开tuple
s:
for score,id,name in scoreAllTheThings(): if score > goodScoreThreshold: print "%6.3f #%6d %s"%(score,id,name)
…因为对象版本更杂乱阅读:
for entry in scoreAllTheThings(): if entry.score > goodScoreThreshold: print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)
…更不用说dict
。
for entry in scoreAllTheThings(): if entry['score'] > goodScoreThreshold: print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])
如果这个东西被广泛的使用,并且你发现自己在代码中的多个地方做了类似的非平凡的操作,那么通常值得使用适当的方法使它成为一个类对象。
最后,如果我要与非Python系统组件交换数据,我经常会把它们保存为dict
因为这最适合于JSON序列化。
在像Python这样的语言中,我通常会使用一个字典,因为它比创建一个新类的开销少。
但是,如果我发现自己经常返回相同的一组变量,那么这可能涉及到一个新的类,我将分解。
我会用一个字典来传递和返回一个函数的值:
使用形式中定义的变量形式 。
form = { 'level': 0, 'points': 0, 'game': { 'name': '' } } def test(form): form['game']['name'] = 'My game!' form['level'] = 2 return form >>> print(test(form)) {u'game': {u'name': u'My game!'}, u'points': 0, u'level': 2}
这是我和处理单元最有效的方式。
你只需要传入一个指针,只返回一个指针。
无论何时在代码中进行更改,您都不必更改函数(其中成千上万个参数)。