关于"在 Python 中返回多个值"的讨论,例如1,2.这不是我试图在这里找到的"多值返回"模式。无论你使用什么(元组、列表、字典、对象(,它仍然是单个返回值,你需要以某种方式解析该返回值(结构(。
多个返回值的真正好处在于升级过程。例如
最初,你有
def func():
return 1
print func() + func()
然后,您决定func()
可以返回一些额外的信息,但您不想破坏以前的代码(或逐个修改它们(。它看起来像
def func():
return 1, "extra info"
value, extra = func()
print value # 1 (expected)
print extra # extra info (expected)
print func() + func() # (1, 'extra info', 1, 'extra info') (not expected, we want the previous behaviour, i.e. 2)
以前的代码(func() + func()
(被破坏了。你必须修复它。
我不知道我是否把问题说清楚了...您可以看到 CLISP 示例。有没有在 Python 中实现这种模式的等效方法?
编辑:我将上述 clisp 片段放到网上供您快速参考。
让我在这里为多个返回值模式提供两个用例。可能有人可以对这两种情况有替代解决方案:
- 更好的支持平滑升级。如上例所示。
- 拥有更简单的客户端代码。请参阅我到目前为止的以下替代解决方案。使用异常可以使升级过程顺利进行,但需要花费更多的代码。
当前的替代方案:(它们不是"多价值回报"结构,但它们可以是满足上面列出的一些要点的工程解决方案(
- 元组、列表、字典、对象。如前所述,您需要从客户端进行某些解析。例如
if ret.success == True: blabla
.在此之前,您需要ret = func()
。写if func() == True: blabal
要干净得多. - 使用
Exception
.正如此线程中所讨论的,当"False"情况很少见时,这是一个很好的解决方案。即使在这种情况下,客户端代码仍然太重。 - 使用参数,例如
def func(main_arg, detail=[])
.根据您的设计,detail
可以是list
的,也可以是dict
的,甚至是对象。func()
仅返回原始简单值。详细信息转到detail
参数。问题是客户端需要在调用之前创建一个变量才能保存详细信息。 - 使用"详细"指示器,例如
def func(main_arg, verbose=False)
.当verbose == False
(默认值;以及客户端使用func()
的方式(时,返回原始简单值。verbose == True
时,返回一个包含简单值和详细信息的对象。 - 使用"版本"指示器。与"冗长"相同,但我们在那里扩展了这个想法。这样,您可以多次升级返回的对象。
- 使用全局
detail_msg
。这就像旧的 C 式error_msg
.这样,函数始终可以返回简单值。必要时,客户端可以参考detail_msg
。可以根据用例将detail_msg
放在全局范围、类范围或对象范围中。 - 使用生成器。
yield simple_return
然后yield detailed_return
.这个解决方案在被叫方方面很好。但是,调用方必须执行类似func().next()
和func().next().next()
。你可以用一个对象包装它并覆盖__call__
以简化它,例如func()()
,但从呼叫者的角度来看,它看起来不自然。 - 对返回值使用包装类。重写类的方法以模拟原始简单返回值的行为。将详细数据放在类中。我们在处理返回类型的项目中采用了
bool
替代方法。查看相关提交:https://github.com/fqj1994/snsapi/commit/589f0097912782ca670568fe027830f21ed1f6fc
(我没有足够的声誉在帖子中放置更多链接... -_-//(
以下是一些解决方案:
- 根据@yupbank的回答,我把它形式化为装饰器,见
github.com/hupili/multiret
- 上面的第 8 个备选方案说我们可以包装一个类。这是我们目前采用的工程解决方案。为了包装更复杂的返回值,我们可能会使用 meta 类按需生成所需的包装类。没有尝试过,但这听起来像是一个强大的解决方案。
尝试检查?
我做了一些尝试,不是很优雅,但至少是可行的......并且:)
import inspect
from functools import wraps
import re
def f1(*args):
return 2
def f2(*args):
return 3, 3
PATTERN = dict()
PATTERN[re.compile('(w+) f()')] = f1
PATTERN[re.compile('(w+), (w+) = f()')] = f2
def execute_method_for(call_str):
for regex, f in PATTERN.iteritems():
if regex.findall(call_str):
return f()
def multi(f1, f2):
def liu(func):
@wraps(func)
def _(*args, **kwargs):
frame,filename,line_number,function_name,lines,index=
inspect.getouterframes(inspect.currentframe())[1]
call_str = lines[0].strip()
return execute_method_for(call_str)
return _
return liu
@multi(f1, f2)
def f():
return 1
if __name__ == '__main__':
print f()
a, b = f()
print a, b
您的案例确实需要代码编辑。但是,如果你需要一个黑客,你可以使用函数属性返回额外的值,而不修改返回值。
def attr_store(varname, value):
def decorate(func):
setattr(func, varname, value)
return func
return decorate
@attr_store('extra',None)
def func(input_str):
func.extra = {'hello':input_str + " ,How r you?", 'num':2}
return 1
print(func("John")+func("Matt"))
print(func.extra)
演示 : http://codepad.org/0hJOVFcC
但是,请注意,函数属性的行为类似于静态变量,您需要小心地为它们分配值,追加和其他修饰符将作用于以前保存的值。
神奇之处在于你应该使用设计模式 blablabla 在处理结果时不使用实际操作,而是使用参数作为操作方法,对于您的情况,您可以使用以下代码:
def x():
#return 1
return 1, 'x'*1
def f(op, f1, f2):
print eval(str(f1) + op + str(f2))
f('+', x(), x())
如果想要针对更复杂情况的通用解决方案,可以扩展 f 函数,并通过 op 参数指定过程操作