想象一下,我有一个带有一些功能的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']
作为默认返回值...
我看到的另一个选项是创建全局变量,但在我的模块中有一堆它们似乎是错误的,我需要记住它们的名字,另外没有附加到函数本身看起来像一个糟糕的设计选择。
这种情况的正确功能设计是什么?
答案 0 :(得分:5)
将常用计算放入其自己的函数中,如Jayanth Koushik建议的那样,如果该计算可以恰当地命名。如果你想从一个函数返回许多值(一个中间结果和一个最终结果),那么一个dict可能是一个过度杀手,取决于你的目标是什么,但在python中,如果你的函数有一个元组,那么简单地返回一个元组就更自然了。要返回的许多值:
def myfunc():
intermediate = 5
result = 6
return intermediate, result
# using the function:
intermediate, result = myfunc()
答案 1 :(得分:5)
通常,当您有两种不同的方式想要返回数据时,请继续执行两个不同的功能。毕竟,“扁平比嵌套更好”。只要一个人打电话给另一个,这样你就不要重复自己了。
例如,在标准库中,urllib.parse
有parse_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,它更漂亮了)
另一个明显的例子是re
:re.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