【问题标题】:How can I find out the number of outputs returned by a python function?如何找出 python 函数返回的输出数量?
【发布时间】:2015-02-23 09:31:21
【问题描述】:

假设有两个 Python 函数:

def f1(x):
    return x

def f2(x):
    return x,x**2

我想知道f1返回了多少输出,f2返回了多少输出。

我想避免以下解决方案:

result = f1(1)
no_outputs = len(result) if type(result) == tuple else 1

由于当函数返回元组时此解决方案失败。

我开始使用 python ast(抽象语法树)库,想知道是否有任何方法可以解析函数的语法树以计算返回的输出数量?

类似的东西:

import inspect
import ast

src = inspect.getsourcelines(f1)[0]

string = ''.join(src)
ast_   = ast.parse(string)

我探索了ast_,但无法找到回报。

【问题讨论】:

  • 你为什么要这样做?
  • 由于f1 是硬编码的,只需查看其文档即可了解它返回的内容。
  • 我正在编写一些代码,这些代码将根据函数f 返回的输出数量来运行。不幸的是,由于复杂的原因,我无法改变f 的工作方式并标准化它的输出(这是正确的做法):)
  • 对于f1f2,您对这个问题的预期输出是什么?
  • Python 是动态类型的。这并不意味着它很神奇,你可以返回任何你想要的值......

标签: python function output abstract-syntax-tree


【解决方案1】:

从技术上讲,函数返回 ONE 值。该值可能是元组、字典或任意复杂的对象(包括集合对象)。

多个返回值的出现仅仅是围绕自动解包的一些语法糖的结果,并且 Python 中的元组运算符实际上是 ,(逗号;括号在 return 中不是必需的 em> 语句,并且仅在 ,(逗号)不明确的情况下才需要)。

因为 Python 是动态类型的,所以函数可以根据其参数或调用时程序的全局状态返回不同类型的值(对象)。它可以在同一个程序(解释器/VM)会话期间为不同的调用返回不同类型的对象。

您应该将函数的返回类型视为文档问题...任何编写良好的 Python 函数都会记录其返回类型(最好在文档字符串中)。

对于包裹在某些数据结构上的类的方法来说,返回 self 是相当常见的......这允许“链式使用”模式:Foo.fiddle().faddle().spritz().output() ...我们在一个简洁的表达式中对对象执行一系列操作。

【讨论】:

  • 谢谢,这很有启发性!
【解决方案2】:

我同意 cmets 的观点,即这是一个有点奇怪的问题......但是,要按照您提出的方法回答您的问题,让我们从以下简化假设开始:

  1. 只有一个返回语句,出现在最后 每个功能的

  2. 逗号可用于确定“输出数量”,即 (x, 2)x, 2 都被认为是两个“输出”

  3. 从第 2 点开始,return 语句由一系列变量(可能是列表或元组)组成,而不是像生成器或列表推导这样更复杂的表达式

我们假设上述情况,否则您几乎肯定需要正确解析有效 Python 代码的子集,例如通过构建 AST。鉴于上述情况,您从inspect.getsourcelines() 返回的列表中取出最后一个字符串并匹配正则表达式:

In [47]: def f2(x):
   ....:     return x, x**2

In [48]: ret2 = inspect.getsourcelines(f2)

In [49]: ret2
Out[49]: ([u'def f2(x):\n', u'    return x, x**2\n'], 1)

In [55]: ret_pattern = r'return\s*(.*)\n*$'

In [56]: out2 = re.findall(ret_pattern, ret2[0][-1])

In [57]: out2
Out[57]: [u'x, x**2']

In [58]: len(out2[0].split(','))
Out[58]: 2

返回变量元组进行测试:

In [59]: def f3(x):
   ....:     return (x, x**2, x**3)
   ....:

In [62]: out3 = re.findall(ret_pattern, inspect.getsourcelines(f3)[0][-1])

In [63]: len(out3[0].split(','))
Out[63]: 3

【讨论】:

  • 一个函数的定义中可以有多个return语句。返回的值将由控制流遇到的第一个值决定。只有一个返回值……它可以是一个容器。如果你有一个像 return a,b 这样的语句,那么 Python 会隐式实例化一个元组并将绑定到 a 和 b 的对象打包到其中。
  • 顺便说一句,您可以从复杂的表达式返回结果,也可以从函数返回生成器。像函数这样的生成器是第一类对象,可以作为参数传递,绑定到名称(变量)或从函数和方法返回,就像任何其他对象一样。
  • 同意,因此简化假设:)
  • 我不提倡做上述任何事情——正如其他 cmets 指出的那样,几乎可以肯定有更好的方法来解决 OP 的问题。从基本/哲学的角度来看,我提出的答案相当愚蠢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-07
  • 2016-09-28
相关资源
最近更新 更多