从 Python 函数返回多个值的替代方法 [已关闭]问题

2022-09-05 01:03:10

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

选项:使用元组

考虑这个简单的例子:

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__

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__

选项:使用数据类(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中列表和元组之间唯一真正的区别是列表是可变的,而元组不是。

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

问题

在冗长的序言之后,出现了一个不可避免的问题。哪种方法(您认为)最好?


答案 1

为此,在 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 +)中,新库获得了NamedTuple类,以使命名元组更易于创建且功能更强大。继承自允许您使用文档字符串、默认值和类型批注。typingtyping.NamedTuple

示例(来自文档):

class Employee(NamedTuple):  # inherit from typing.NamedTuple
    name: str
    id: int = 3  # default value

employee = Employee('Guido')
assert employee.id == 3

答案 2

对于小型项目,我发现使用元组最容易。当这变得太难管理(而不是之前)时,我开始将事物分组为逻辑结构,但是我认为您建议使用字典和对象是错误的(或过于简单化)。ReturnValue

返回带有键 、 、 等的字典与元组相比没有任何优势。返回具有属性 、 、 等的实例也不会比元组具有任何优势。如果你想到达任何地方,你需要开始命名东西,无论如何你都可以使用元组来做到这一点:"y0""y1""y2"ReturnValue.y0.y1.y2

def get_image_data(filename):
    [snip]
    return size, (format, version, compression), (width,height)

size, type, dimensions = get_image_data(x)

恕我直言,元组之外唯一好的技术是使用适当的方法和属性返回真实对象,就像从 或 获得一样。re.match()open(file)