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)
- 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 toret = func()
before that. It's much cleaner to writeif func() == True: blabal
. - 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. - Use an arg, e.g.
def func(main_arg, detail=[])
. Thedetail
can belist
ordict
or even an object depending on your design. Thefunc()
returns only original simple value. Details go to thedetail
argument. Problem is that the client need to create a variable before invocation in order to hold the details. - Use a "verbose" indicator, e.g.
def func(main_arg, verbose=False)
. Whenverbose == False
(default; and the way client is usingfunc()
), return original simple value. Whenverbose == True
, return an object which contains simple value and the details. - 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.
- Use global
detail_msg
. This is like the old C-styleerror_msg
. In this way, functions can always return simple values. The client side can refer todetail_msg
when necessary. One can putdetail_msg
in global scope, class scope, or object scope depending on the use cases. - Use generator.
yield simple_return
and thenyield detailed_return
. This solution is nice in the callee's side. However, the caller has to do something likefunc().next()
andfunc().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. - 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.