如何从函数返回多个值? [关闭]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何从函数返回多个值? [关闭]相关的知识,希望对你有一定的参考价值。

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

Option: Using a tuple

考虑这个简单的例子:

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

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

Option: Using a dictionary

下一个合乎逻辑的步骤似乎是引入某种“记录符号”。在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']

Option: Using a class

但是,还有另一种选择。我们可以返回一个专门的结构。我已经在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__属性。该课程可表示为:

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__

Option: Using a dataclass (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)

Option: Using a list

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

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

这是我最不喜欢的方法。我想我已经被Haskell暴露了,但混合型列表的想法一直让我觉得不舒服。在这个特定的例子中,列表是-not-混合类型,但可以想象它可以。

就我所知,以这种方式使用的列表实际上并没有获得关于元组的任何信息。 Python中列表和元组之间唯一真正的区别是列表是mutable,而元组则不是。

我个人倾向于从函数式编程中继承惯例:使用相同类型的任意数量元素的列表,以及预定类型的固定数量元素的元组。

Question

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

我通常发现自己去了字典路线,因为它涉及较少的设置工作。但是,从类型的角度来看,你可能最好不要走类路线,因为这可以帮助你避免混淆字典所代表的内容。

另一方面,Python社区中有一些人感觉implied interfaces should be preferred to explicit interfaces,此时对象的类型确实不相关,因为你基本上依赖于同一属性总是具有相同含义的约定。

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

答案

为此目的,Named tuples在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
另一答案

Python的元组,dicts和对象为程序员提供了在小型数据结构(“事物”)的形式和便利之间的平滑权衡。对我来说,如何表示事物的选择主要取决于我将如何使用该结构。在C ++中,将struct用于仅数据项目和class用于具有方法的对象是常见的约定,即使您可以合法地将方法放在struct上;我的习惯在Python中是相似的,dicttuple代替struct

对于坐标集,我将使用tuple而不是点classdict(并注意您可以使用tuple作为字典键,因此dicts可以制作出色的稀疏多维数组)。

如果我要迭代一系列事物,我更喜欢在迭代中解压缩tuples:

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序列化。

另一答案

关于S.Lott建议使用命名容器类的+1。

对于Python 2.6及更高版本,named tuple提供了一种轻松创建这些容器类的有用方法,结果是“轻量级,并且不需要比常规元组更多的内存”。

另一答案

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

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

另一答案

我会使用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}

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

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

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

另一答案

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

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

正如智者所说:

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

另一答案

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

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

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

另一答案

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

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

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

另一答案

我更喜欢:

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

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

另一答案
>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3
另一答案

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

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

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

以上是关于如何从函数返回多个值? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何从 FragmentPagerAdapter 返回多个片段

如何访问从sql函数获取的java中的多个返回值

如何从 Python 中的函数返回多个值? [复制]

从使用 wkwebview 返回值的 javascript 调用 swift 函数

如何从片段返回主要活动

我如何从c ++中的函数返回多个值[重复]