0

If function returns a two value list or tuple on success or False on failure, how can I best unpack the return list into two variables while also checking for False?

def get_key_value():
  if (cond != True):
    return False
  return [val1, val2]

# Call it
# How can I also check for False while unpacking?
key, value = get_key_value()
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390

4 Answers4

4

Coverting @Felix Kling's great comment into an answer.

If not being able to find a (key, value) pair indicates some kind of system failure, it would be better to throw an exception. If your failure doesn't really fall into any of the standard exceptions, you should build a new exception type of your own.

The cond != True is better written as not cond. Also it's better to not create a list if it's not necessary.

class DataNotFound(Exception): pass

def get_key_value():
  if not cond:
    raise DataNotFound("Couldn't find it!")
  return val1, val2

try:
    key,value = get_key_value()
except DataNotFound:
    #handle the failure somehow
    key, value = 'ERROR', 'ERROR'
bukzor
  • 37,539
  • 11
  • 77
  • 111
  • 1
    +1 Just two details: `if not cond:` and `return (val1, val2)`. –  Mar 10 '11 at 21:43
  • 1
    I agree with @delnan, `if not cond` is MUCH preferred over `if cond != True`. As for the return, even just `return val1, val2` will suffice. – PaulMcG Mar 10 '11 at 22:12
3

This falls under the "Easier to Ask for Forgiveness than Permission" policy of Python. I avoid catching TypeError in your function, in case there's some other unforeseen problem.

data = get_key_value()
try:
   key, value = data
except TypeError:
   #handle the failure somehow
   key, value = 'ERROR', 'ERROR'
bukzor
  • 37,539
  • 11
  • 77
  • 111
1

I don't think there is an idiomatic way to do this -- not least because a function that behaves that way is itself unidiomatic. If you have to do it, I suggest you simply make use of the fact that your 2-element list or tuple is a "truthy" rather than a "falsy" value (this isn't Python terminology but it's useful):

pair_or_false = get_key_value()
if pair:
    key,value = val
else:
    # handle failure in whatever way

The obvious alternative is to treat the not-found case as an exception:

try:
    key,value = get_key_value()
except TypeError:
    # deal with not-found case

but if there's any possibility at all that something other than the unsuccessful unpacking could raise a TypeError then you run the risk of masking a genuine error that way.

Gareth McCaughan
  • 19,888
  • 1
  • 41
  • 62
  • I had been dealing with it using your first `if/else` method but hoped to reduce my linecount since the function is called many times. I'll see how it works out catching `TypeError`. The application is fairly straightforward DOM parsing and the risk is low for other `TypeErrors`. Thanks. – Michael Berkowski Mar 10 '11 at 21:44
1

You're running into problems because you're mixing return types. Just because you can doesn't mean you should.

Although I agree with the others here that an exception is one appropriate way to go, it may depend on whether you expect to find a valid key & value most of the time. If so, use an exception (something like KeyError) to indicate that the function failed. But if you expect it to fail at a high rate, you may not want the exception overhead. In that case, return something like [None, None] from get_key_value and then your calling code would look like:

key, value = get_key_value()
if key:
    # take action
else:
    # handle the error appropriately
Brandon
  • 3,684
  • 1
  • 18
  • 25