首页 文章

你如何在Python中返回多个值? [关闭]

提问于
浏览
868

在支持它的语言中返回多个值的规范方法通常是tupling .

选项:使用元组

考虑这个简单的例子:

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0,y1,y2)

但是,随着返回值的增加,这很快就会出现问题 . 如果您想要返回四个或五个值,该怎么办?当然,你可以保持它们的组合,但很容易忘记哪个值在哪里 . 在任何想要接收它们的地方解压缩它们也是相当丑陋的 .

选项:使用字典

下一个合乎逻辑的步骤似乎是介绍某种'record notation' . 在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 Reference Manual

slots__声明采用一系列实例变量,并在每个实例中保留足够的空间来保存每个变量的值 . 保存空间是因为没有为每个实例创建__dict .

选项:使用列表

我忽略的另一个建议来自比尔蜥蜴:

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

这是我最不喜欢的方法 . 据我所知,我想我在元组方面获得了任何东西 . Python中列表和元组之间唯一真正的区别是列表是mutable,而tlesles则不是 . 我个人倾向于从函数式编程中继承惯例:对任何数量的相同类型的元素使用列表,对于预定类型的固定数量的元素使用元组 .

问题

在冗长的序言之后,出现了不可避免的问题 . 你觉得哪种方法最好?

我通常发现自己去了字典路线,因为它涉及较少的设置工作 . 但是从类型的角度来看,你可能会更好地走上课程路线,因为这可以帮助你避免混淆字典所代表的内容 . 另一方面,Python社区中有一些感觉implied interfaces should be preferred to explicit interfaces,此时对象的类型实际上并不依赖于相同属性将始终具有相同含义的约定 .

那么,你如何在Python中返回多个值?

14 回答

  • 22

    为此目的,在2.6中添加了Named tuples . 另请参阅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
    
  • 3

    我投票给字典 .

    我发现如果我创建一个返回超过2-3个变量的函数,我会将它们折叠起来放在字典中 . 否则我倾向于忘记我正在返回的顺序和内容 .

    此外,引入“特殊”结构会使您的代码更难以遵循 . (其他人将不得不搜索代码以找出它是什么)

    如果您关注类型查找,请使用描述性字典键,例如“x-values list” .

    def g(x):
      y0 = x + 1
      y1 = x * 3
      y2 = y0 ** y3
      return {'y0':y0, 'y1':y1 ,'y2':y2 }
    
  • 14
    >>> def func():
    ...    return [1,2,3]
    ...
    >>> a,b,c = func()
    >>> a
    1
    >>> b
    2
    >>> c
    3
    
  • 22

    在像Python这样的语言中,我通常会使用字典,因为它比创建新类所需的开销更少 .

    但是,如果我发现自己经常返回相同的变量集,那么这可能涉及一个我将要考虑的新类 .

  • 33

    很多答案都表明你需要返回某种类型的集合,比如字典或列表 . 你可以省去额外的语法,然后写出以逗号分隔的返回值 . 注意:这在技术上返回一个元组 .

    def f():
        return True, False
    x, y = f()
    print(x)
    print(y)
    

    得到:

    True
    False
    
  • 3

    Python 's tuples, dicts, and objects offer the programmer a smooth tradeoff between formality and convenience for small data structures ( 122361 ). For me, the choice of how to represent a thing is dictated mainly by how I' m将使用该结构 . 在C中,将 struct 用于仅数据项目和 class 用于具有方法的对象是一种常见约定,即使您可以合法地将方法放在 struct 上;我的习惯在Python中类似, dicttuple 代替 struct .

    对于坐标集,我将使用 tuple 而不是点 classdict (并注意您可以使用 tuple 作为字典键,因此 dict s很棒稀疏多维数组) .

    如果我要迭代一系列事情,我更喜欢在迭代中解包 tuple

    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'])
    

    如果事物被广泛使用,并且你发现自己在代码中的多个位置对它进行类似的非平凡操作,那么通常使用适当的方法使它成为一个类对象是值得的 .

    最后,如果我最常将它们保存在 dict 中,因为它最适合JSON序列化 .

  • 62

    每当元组感觉“自然”时,我更喜欢使用元组;坐标是一个典型的例子,其中单独的对象可以独立存在,例如,在单轴仅缩放计算中,顺序很重要 . 注意:如果我可以对项目进行排序或改组而不会对组的含义产生负面影响,那么我可能不应该使用元组 .

    仅当分组对象不总是相同时,才使用字典作为返回值 . 想想可选的邮箱 Headers .

    对于其他情况,如果分组对象在组内部具有固有含义,或者需要具有自己方法的完全成熟对象,则使用类 .

  • 178

    1.关于S.Lott关于命名容器类的建议 .

    对于python 2.6及更高版本,named tuple提供了一种轻松创建这些容器类的有用方法,结果为"lightweight and require no more memory than regular tuples" .

  • 552

    我会使用dict来传递和返回函数的值:

    使用form中定义的变量形式 .

    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}
    

    这对我和处理单元来说是最有效的方式 .

    你必须只传入一个指针并返回一个指针 .

    无论何时在代码中进行更改,都不必更改函数(数千个)参数 .

  • 16

    通常,“专用结构”实际上是对象的合理当前状态,具有其自己的方法 .

    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
    

    我喜欢在可能的情况下找到匿名结构的名称 . 有意义的名字使事情变得更加清晰 .

  • 1

    “最佳”是一个部分主观的决定 . 在可接受不可变的一般情况下,使用元组作为小返回集 . 当不需要可变性时,元组总是优于列表 .

    对于更复杂的返回值,或者对于形式有 Value 的情况(即高 Value 代码),命名元组更好 . 对于最复杂的情况,对象通常是最好的 . 然而,重要的是情况 . 如果返回一个对象是有意义的,因为这是你在函数结束时自然拥有的(例如工厂模式)然后返回对象 .

    正如智者所说:

    过早优化是编程中所有邪恶(或至少大部分)的根源 .

  • 25

    另一种选择是使用发电机:

    >>> 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
    

    虽然IMHO元组通常是最好的,除非返回的值是类中封装的候选者 .

  • 15

    对于小项目,我发现使用元组最容易 . 当它变得难以管理(而不是之前)时,我开始将事物分组为逻辑结构,但是我认为你建议使用字典和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) 获得 .

  • 148

    我更喜欢

    def g(x):
      y0 = x + 1
      y1 = x * 3
      y2 = y0 ** y3
      return {'y0':y0, 'y1':y1 ,'y2':y2 }
    

    似乎其他一切只是额外的代码来做同样的事情 .

相关问题