从Python中的函数返回中间结果

时间:2014-02-26 06:21:32

标签: python function

想象一下,我有一个带有一些功能的Python模块:

def sumvars(x, y, z):
    s = x
    s += y
    s += z
    return s

但有时我想得到一些中间计算的结果(例如,我可以有一个反转矩阵的函数,并且想知道已经计算为中间步骤的行列式)。显然,如果已经在该函数中完成了这些计算,我不想再重做那些计算。

我的第一个想法是返回一个字典:

def sumvars(x, y, z):
    d = {}
    s = x
    d['first_step'] = s
    s += y
    d['second_step'] = s
    s += z
    d['final'] = s
    return d

但是我不记得numpy或scipy中的任何返回dicts的函数,所以看起来这可能不是一个好主意。 (为什么?)通常情况下,我总是要输入sumvars(x,y,z)['final']作为默认返回值...

我看到的另一个选项是创建全局变量,但在我的模块中有一堆它们似乎是错误的,我需要记住它们的名字,另外没有附加到函数本身看起来像一个糟糕的设计选择。

这种情况的正确功能设计是什么?

8 个答案:

答案 0 :(得分:5)

将常用计算放入其自己的函数中,如Jayanth Koushik建议的那样,如果该计算可以恰当地命名。如果你想从一个函数返回许多值(一个中间结果和一个最终结果),那么一个dict可能是一个过度杀手,取决于你的目标是什么,但在python中,如果你的函数有一个元组,那么简单地返回一个元组就更自然了。要返回的许多值:

def myfunc():
    intermediate = 5
    result = 6
    return intermediate, result

# using the function:
intermediate, result = myfunc()

答案 1 :(得分:5)

通常,当您有两种不同的方式想要返回数据时,请继续执行两个不同的功能。毕竟,“扁平比嵌套更好”。只要一个人打电话给另一个,这样你就不要重复自己了。

例如,在标准库中,urllib.parseparse_qs(返回dict)和parse_qsl(返回list)。 parse_qs然后调用另一个:

def parse_qs(...):

    parsed_result = {}
    pairs = parse_qsl(qs, keep_blank_values, strict_parsing,
                      encoding=encoding, errors=errors)
    for name, value in pairs:
        if name in parsed_result:
            parsed_result[name].append(value)
        else:
            parsed_result[name] = [value]
    return parsed_result

非常简单。所以在你的例子中,似乎很好

def sumvars(x, y, z):
    return sumvars_with_intermediates(x, y, z).final

def sumvars_with_intermediates(x, y, z):
    ...
    return my_namedtuple(final, first_step, second_step)

(我赞成从我的API返回namedtuple而不是dict s,它更漂亮了)

另一个明显的例子是rere.findall是它自己的函数,而不是search的一些配置标志。

现在,标准库是许多作者制作的庞大的东西,所以你会发现每个例子的反例。你会经常看到上面的模式,而不是一个接受一些配置标志的综合函数,我发现它更具可读性。

答案 2 :(得分:4)

不确定功能属性是否是个好主意:

In [569]: def sumvars(x, y, z):
     ...:     s = x
     ...:     sumvars.first_step = s
     ...:     s += y
     ...:     sumvars.second_step = s
     ...:     s += z
     ...:     return s


In [570]: res=sumvars(1,2,3)
     ...: print res, sumvars.first_step, sumvars.second_step
     ...: 
6 1 3

注意:正如@BrenBarn所提到的,这个想法就像全局变量,当你想重用它们时,你以前计算的“中间结果”无法存储。

答案 3 :(得分:2)

刚想出这个想法可能是一个更好的解决方案:

def sumvars(x, y, z, mode = 'default'):
    d = {}
    s = x
    d['first_step'] = s
    s += y
    d['second_step'] = s
    s += z
    d['final'] = s
    if mode == 'default':
        return s
    else:
        return d

答案 4 :(得分:2)

我相信正确的解决方案是使用一个类,以便更好地掌握你的建模。例如,在Matrix的情况下,您可以简单地将行列式存储在"行列式"属性。

以下是使用矩阵示例的示例。

class Matrix:
    determinant = 0

    def calculate_determinant(self):
        #calculations
        return determinant

    def some_method(self, args):
       # some calculations here

       self.determinant = self.calculate_determinant()

       # other calculations

 matrix = Matrix()
 matrix.some_method(x, y, z)
 print matrix.determinant

这也允许您将方法分离为更简单的方法,例如用于计算矩阵行列式的方法。

答案 5 :(得分:2)

另一种变化:

def sumvars(x, y, z, d=None):
  s = x
  if not d is None:
    d['first_step'] = s
  s += y
  if not d is None:
    d['second_step'] = s
  s += z
  return s

该函数始终返回所需的值,而不将其打包到元组或字典中。中间结果仍然可用,但仅在请求时提供。电话

sumvars(1, 2, 3)

只返回6而不存储中间值。但是电话

d = {}
sumvars(1, 2, 3, d)

返回相同的答案6 将中间计算插入到提供的字典中。

答案 6 :(得分:1)

选项1。制作两个单独的功能。

选项2。使用生成器:

>>> def my_func():
...     yield 1
...     yield 2
... 
>>> result_gen = my_func()
>>> result_gen
<generator object my_func at 0x7f62a8449370>
>>> next(result_gen)
1
>>> next(result_gen)
2
>>> next(result_gen)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

答案 7 :(得分:0)

受@zhangxaochen解决方案的启发,以下是我对使用类属性的问题的看法:

class MyClass():
    def __init__(self):
        self.i = 4

    def f(self):
        s = self.i
        MyClass.first_step = s
        print(MyClass.first_step)

        s += self.i
        MyClass.second_step = s
        print(MyClass.second_step)

        s += self.i
        return s

def main():
    x = MyClass()
    print(x.f()) # print final s
    print(x.first_step)
    print(x.second_step)
    print(MyClass.second_step)

注意:我提供了一些印刷品,以使其更明确地说明如何检索属性值。

结果:

4
8
12
4
8
8