0

There were several discussions on "returning multiple values in Python", e.g. 1, 2. This is not the "multiple-value-return" pattern I'm trying to find here. No matter what you use (tuple, list, dict, an object), it is still a single return value and you need to parse that return value (structure) somehow.

The real benefit of multiple return value is in the upgrade process. For example,

originally, you have

def func():
    return 1
print func() + func()

Then you decided that func() can return some extra information but you don't want to break previous code (or modify them one by one). It looks like

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)

The previous codes (func() + func()) are broken. You have to fix it.

I don't know whether I made the question clear... You can see the CLISP example. Is there an equivalent way to implement this pattern in Python?

EDIT: I put the above clisp snippets online for your quick reference.


Let me put two use cases here for multiple return value pattern. Probably someone can have alternative solutions to the two cases:

  • Better support smooth upgrade. This is shown in the above example.
  • Have simpler client side codes. See following alternative solutions I have so far. Using exception can make the upgrade process smooth but it costs more codes.

Current alternatives: (they are not "multi-value-return" constructions, but they can be engineering solutions that satisfy some of the points listed above)

  1. tuple, list, dict, an object. As is said, you need certain parsing from the client side. e.g. if ret.success == True: blabla. You need to ret = func() before that. It's much cleaner to write if func() == True: blabal.
  2. Use Exception. As is discussed in this thread, when the "False" case is rare, it's a nice solution. Even in this case, the client side code is still too heavy.
  3. Use an arg, e.g. def func(main_arg, detail=[]). The detail can be list or dict or even an object depending on your design. The func() returns only original simple value. Details go to the detail argument. Problem is that the client need to create a variable before invocation in order to hold the details.
  4. Use a "verbose" indicator, e.g. def func(main_arg, verbose=False). When verbose == False (default; and the way client is using func()), return original simple value. When verbose == True, return an object which contains simple value and the details.
  5. Use a "version" indicator. Same as "verbose" but we extend the idea there. In this way, you can upgrade the returned object for multiple times.
  6. Use global detail_msg. This is like the old C-style error_msg. In this way, functions can always return simple values. The client side can refer to detail_msg when necessary. One can put detail_msg in global scope, class scope, or object scope depending on the use cases.
  7. Use generator. yield simple_return and then yield detailed_return. This solution is nice in the callee's side. However, the caller has to do something like func().next() and func().next().next(). You can wrap it with an object and override the __call__ to simplify it a bit, e.g. func()(), but it looks unnatural from the caller's side.
  8. Use a wrapper class for the return value. Override the class's methods to mimic the behaviour of original simple return value. Put detailed data in the class. We have adopted this alternative in our project in dealing with bool return type. see the relevant commit: https://github.com/fqj1994/snsapi/commit/589f0097912782ca670568fe027830f21ed1f6fc (I don't have enough reputation to put more links in the post... -_-//)

Here are some solutions:

  • Based on @yupbank 's answer, I formalized it into a decorator, see github.com/hupili/multiret
  • The 8th alternative above says we can wrap a class. This is the current engineering solution we adopted. In order to wrap more complex return values, we may use meta class to generate the required wrapper class on demand. Have not tried, but this sounds like a robust solution.
4

3 回答 3

1

尝试检查?

我做了一些尝试,不是很优雅,但至少是可行的..并且有效:)

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             
于 2013-07-16T15:25:52.663 回答
0

神奇的是您应该使用设计模式 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 参数指定流程操作

于 2013-07-15T07:41:25.607 回答
0

您的案例确实需要代码编辑。但是,如果您需要 hack,您可以使用函数属性来返回额外的值,而无需修改返回值。

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

但是,请注意函数属性的行为类似于静态变量,您需要小心地为它们赋值,附加和其他修饰符将作用于以前保存的值。

于 2013-07-15T07:28:47.607 回答