Elias Bachaalany
Elias Bachaalany

Reputation: 1200

Function returning a tuple or None: how to call that function nicely?

Suppose the following:

def MyFunc(a):
  if a < 0:
    return None
  return (a+1, a+2, a+3)

v1, v2, v3 = MyFunc()
# Bad ofcourse, if the result was None

What is the best way to define a function that returns a tuple and yet can be nicely called. Currently, I could do this:


r = MyFunc()
if r:
  v1, v2, v3 = r
else:
  # bad!!
  pass

What I don't like about this is that I have to use a single variable and then unpack it.

Another solution is I could have the function return a tuple full of Nones so that the caller can nicely unpack....

Anyone can suggest a better design?

Upvotes: 14

Views: 10463

Answers (6)

Rajesh
Rajesh

Reputation: 11

This is similar to the previous answer. You could return either an object instance or None

def MyFunc(a):
    class MyFuncClass(object):
        def __init__(self, **kwargs):
            self.__dict__.update(kwargs)
    if a < 0:
        return None
    return MyFuncClass(one=a+1, two=a+2, three=a+3)

o = MyFunc(1)
if o is not None:
    print o.one, o.two, o.three

Upvotes: 1

efotinis
efotinis

Reputation: 14961

If you want the v1, v2, v3 objects to exist and be set to a default value in the case of an error, return the default values yourself. This will make the calling code simpler by not relying on the caller to set them manually:

def MyFunc(a):
    if a < 0:
        # can't use a negative value; just return some defaults
        return (None, None, None)
    return (a+1, a+2, a+3)

On the other hand, if a default return is not appropriate and a negative argument is considered a serious error, raise an exception:

def MyFunc(a):
    if a < 0:
        # sorry, negative values are unacceptable
        raise ValueError('cannot accept a negative value')
    return (a+1, a+2, a+3)

On the third hard, returning None may be preferable sometimes when returning a single object, as is the case with the search() and match() functions of the re module. It somehow stands between the first two cases, because matching failure is an expected outcome, while a default return object wouldn't be very useful anyway.

Upvotes: 2

Skilldrick
Skilldrick

Reputation: 70819

How about raise an ArgumentError? Then you could try calling it, and deal with the exception if the argument is wrong.

So, something like:

try:
    v1, v2, v3 = MyFunc()
except ArgumentError:
    #deal with it

Also, see katrielalex's answer for using a subclass of ArgumentError.

Upvotes: 12

S.Lott
S.Lott

Reputation: 391846

Another solution is I could have the function return a tuple full of Nones so that the caller can nicely unpack....

What's wrong with that? Consistency is a good thing.

Upvotes: 4

Katriel
Katriel

Reputation: 123632

recursive has a truly elegant and Pythonic solution. BUT: why do you want to return None? Python has a way of handling errors, and that is by raising an exception:

class AIsTooSmallError( ArgumentError ): pass

and then

raise AIsTooSmallError( "a must be positive." )

The reason this is better is that returning a value indicates that you have completed processing and are passing back your answer. This is fine if you have done some processing, but it's silly if you are immediately returning None.

Upvotes: 8

recursive
recursive

Reputation: 86084

This should work nicely:

v1, v2, v3 = MyFunc() or (None, None, None)

When MyFunc() returns a tuple, it will be unpacked, otherwise it will be substituted for a 3-tuple of None.

Upvotes: 10

Related Questions