Reputation: 151
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:
Current alternatives: (they are not "multi-value-return" constructions, but they can be engineering solutions that satisfy some of the points listed above)
if ret.success == True: blabla
. You need to ret = func()
before that. It's much cleaner to write if func() == True: blabal
. 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. 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.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.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. 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.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:
github.com/hupili/multiret
Upvotes: 0
Views: 2675
Reputation: 265
try inspect?
i did some try, and not very elegant, but at least is doable.. and works :)
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
Upvotes: 1
Reputation: 1042
the magic is you should use design pattern blablabla to not use actual operation when you process the result, but use a parameter as the operation method, for your case, you can use the following code:
def x():
#return 1
return 1, 'x'*1
def f(op, f1, f2):
print eval(str(f1) + op + str(f2))
f('+', x(), x())
if you want generic solution for more complicated situation, you can extend the f function, and specify the process operation via the op parameter
Upvotes: 0
Reputation: 43235
Your case does need code editing. However, if you need a hack, you can use function attributes to return extra values , without modifying return values.
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)
Demo : http://codepad.org/0hJOVFcC
However, be aware that function attributes will behave like static variables, and you will need to assign values to them with care, appends and other modifiers will act on previous saved values.
Upvotes: 0