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

2024-11-27 10:43:00
admin
原创
12
摘要:问题描述:在支持它的语言中,返回多个值的规范方法通常是元组。选项:使用元组考虑这个简单的例子:def f(x): y0 = x + 1 y1 = x * 3 y2 = y0 ** y3 return (y0, y1, y2) 然而,随着返回值数量的增加,这很快就会出现问题。如果你想返回四个或五...

问题描述:

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

选项:使用元组

考虑这个简单的例子:

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+)中,新typing库获得了该类NamedTuple,使命名元组更易于创建且功能更强大。继承typing.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)

解决方案 3:

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

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

给出:

True
False

解决方案 4:

我投票支持这本字典。

我发现,如果我创建一个返回超过 2-3 个变量的函数,我会将它们折叠到字典中。否则我很容易忘记返回内容的顺序和内容。

此外,引入“特殊”结构会使您的代码更难理解。(其他人必须搜索代码才能找出它是什么)

如果您担心类型查找,请使用描述性字典键,例如“x 值列表”。

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

解决方案 5:

另一种选择是使用生成器:

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

尽管在我看来元组通常是最好的,但返回的值适合封装在类中的情况除外。

解决方案 6:

我更喜欢:

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

看起来其他一切都只是完成同样事情的额外代码。

解决方案 7:

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

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

对于其余的情况,当分组对象在组内具有固有含义或者需要具有自身方法的成熟对象时,我使用类。

解决方案 8:

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

解决方案 9:

一般来说,“专门的结构”实际上是对象的合理当前状态,具有其自己的方法。

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

我喜欢尽可能为匿名结构寻找名称。有意义的名称使事情更加清晰。

解决方案 10:

Python 的元组、字典和对象为程序员提供了一种在小型数据结构(“事物”)的正式性和便利性之间的平衡。对我来说,如何表示事物的选择主要取决于我将如何使用该结构。在 C++ 中,struct对于纯数据项和class具有方法的对象,使用 是一种常见的惯例,尽管你可以合法地将方法放在 上struct;我在 Python 中的习惯是类似的,用dicttuple代替struct

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

如果我要迭代列表,我更喜欢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'])

如果该事物被广泛使用,并且你发现自己在代码中的多个地方对其执行类似的非平凡操作,那么通常值得将其变成具有适当方法的类对象。

最后,如果我要与非 Python 系统组件交换数据,我通常会将它们保存在中,dict因为这最适合 JSON 序列化。

解决方案 11:

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

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

解决方案 12:

“最佳”是一个部分主观的决定。在可接受不可变的一般情况下,对小返回集使用元组。当可变性不是必需时,元组总是比列表更可取。

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

正如那位智者所说:

过早优化是编程中一切罪恶的根源(或者至少是大部分罪恶的根源)。

解决方案 13:

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

然而,如果我发现自己不断地返回同一组变量,那么这可能涉及到一个我将分解出来的新类。

解决方案 14:

我将使用字典来从函数传递和返回值:

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

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

您只需传入一个指针并返回一个指针即可。

当您的代码发生更改时,您不必更改函数(数千个)的参数。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   657  
  如何借鉴华为IPD体系优化企业研发?在当今竞争激烈的市场环境中,企业要想保持技术领先和产品竞争力,必须拥有一套高效且严谨的研发管理体系。华为作为全球领先的ICT解决方案提供商,其集成产品开发(IPD, Integrated Product Development)体系与质量管理体系(如ISO 9000系列)的融合实践,...
IPD项目管理   15  
  IPD流程图的7种经典绘制方法详解在产品开发领域,集成产品开发(Integrated Product Development,简称IPD)流程被广泛应用,以提高产品开发的效率和质量。IPD流程图作为这一流程的可视化工具,其绘制方法至关重要。本文将详细介绍七种经典的IPD流程图绘制方法,帮助项目管理人员和团队更好地理解和...
IPD研发管理体系   18  
  IPD流程:企业创新管理的核心引擎在当今快速变化的市场环境中,企业要想持续保持竞争力,就必须不断进行创新。而IPD(Integrated Product Development,集成产品开发)流程作为一种先进的产品开发管理模式,正逐渐成为众多企业提升创新能力、加速产品上市速度、降低开发成本的重要选择。本文将深入探讨IP...
IPD管理   18  
  IPD流程与传统产品开发流程的概述在产品开发领域,企业不断寻求高效、系统的管理方法以确保产品能够顺利从概念转化为市场成功的产品。集成产品开发(Integrated Product Development,简称IPD)流程与传统产品开发流程是两种截然不同的管理理念和方法。传统产品开发流程往往以职能部门为核心,各部门按顺序...
IPD流程中PDCP是什么意思   16  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用