Better "return if not None" in Python
Question:
Is there a better way to write this code in python?
result = slow_function()
if result:
return result
[...]
The function slow_function
can return a value or None
and it’s slow, so this is not feasible:
if slow_function():
return slow_function()
There is nothing wrong with the first way, but using a temporary variable seems overkill for python.
This code is pretty useful when you are solving a problem using recursive calls over f
and with local assumption, for example you select an item from a list and then check if there is a feasible solution, otherwise you have to choose another one. Something like:
def f(n):
for x in xrange(n):
result = slow_function(x):
if result:
return result
[...]
Wouldn’t it be better something more idiomatic like:
def f(n):
for x in xrange(n):
return slow_function(x) if is not None
This can be extended to check any kind of value. It would be an easy-to-read return if statement.
Additional example for code lovers
Imagine you have a list of lists of numbers:
lists = [[1,2,3],[4,5],[6,7,8],[9,10],...]
and you want to select one item for each list such that there is at most one even number in the selection. There could be a lot of lists, so trying each combination would be wasteful since you can already tell that if you start selecting [1,2,4,…] there could be no feasible solutions.
def check(selected):
even_numbers = filter(lambda n: (n % 2) == 0, selected)
return len(even_numbers) < 2
def f(lists, selected=[]):
if not lists:
return selected
for n in lists[0]:
if check(selected + [n]):
result = f(lists[1:], selected + [n])
if result:
return result
Wouldn’t it be better a syntax like:
def f(lists, selected=[]):
return selected if not lists
for n in lists[0]:
if check(selected + [n]):
return f(lists[1:], selected + [n]) if is not None
The best I’ve done so far is to turn the function in a generator of feasible solutions:
def f(lists, selected=[]):
if not lists:
yield selected
else:
for n in lists[0]:
if check(selected + [n]):
for solution in f(lists[1:], selected + [n]):
yield solution
Answers:
Without knowing what else you might want to return there are a few options.
-
You could just return the result of the function, None
or not:
return slow_function()
In this, you rely on the caller knowing what to do with the None
value, and really just shift where your logic will be.
-
If you have a default value to return instead of None, you can do this:
return slow_function() or default
In this above, if slow_function
is None
(which is “falsy”) it will return the latter value, otherwise, if slow_function
returns a “truthy” value it will return that. Beware, if slow_function
can return other “falsy” values, like False
, []
or 0, those will be ignored.
-
Alternatively, sometimes what you have is perfectly valid code. You want to compare against a value, and if it is value, return it. The code you have is obvious in what it does, and sometimes that is more important than the “cleverness” of the code.
As per the comments, if your code must continue running if the value is None
then the most obvious way to do it is store it as a temporary value. But, thats not a bad thing, as it reads cleanly.
- Compute a value and store it as the result
- If there is a valid result, return it.
- Otherwise, keep doing things to get a better result.
Better is usually very subjective, and I can’t see any obvious ways to improve this from a computation perspective, and as written it is very human readable which is a clear advantage. Other solutions may be shorter or cleverer, but human readability is often an over looked advantage for code.
Essentially you want to evaluate an expression and then use it twice without binding it to a local variable. The only way to do that, since we don’t have anonymous variables, is to pass it into a function. Fortunately, the control flow for whether the current function returns isn’t controlled by the functions it calls… however, exceptions do propagate up the call stack.
I wouldn’t say this is better, but you could abuse exceptions to get what you want. This should never really be used and it’s more an exercise in curiosity. The result would end up looking like this (note the use of the decorator):
def slow_function(x):
if x % 5 == 0:
return x * 200
@if_returner
def foobme(l):
for i in l:
print "Checking %s..." % (i,)
return_if(slow_function(i))
print foobme([2, 3, 4, 5, 6])
Output is:
Checking 2...
Checking 3...
Checking 4...
Checking 5...
1000
The trick is to piggy-back on exception handling, since those propagate across function calls. If you like it, here’s the implementation:
class ReturnExc(Exception):
def __init__(self, val):
self.val = val
def return_if(val):
if val is not None:
raise ReturnExc(val)
def if_returner(f):
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except ReturnExc, e:
return e.val
return wrapped
What you have written looks fine, but if you wanted to avoid multiple return statements you could do something like this:
def f():
result = slow_function()
if result is None:
[...]
result = [...]
return result
For the problem where slow_function
is operating over a loop, a generator expression would seem the way to go. In Python 3 everything here is lazy, so you get your filter for free:
f = filter(slow_function(x) for x in range(...))
In Python 2 you just need itertools:
from itertools import ifilter
f = ifilter(slow_function(x) for x in xrange(...))
Each iteration will only take place when you ask for it. If you need to continue the operation if the function returns false, then you need the False
ness as a sentinel, so your solution is fine:
def f():
for x in xrange(...):
sentinel = slow_function(x)
if sentinel:
return sentinel
# continue processing
or you can save yourself a variable by using a generator here, too:
from itertools import imap
def f():
for x in imap(slow_function, xrange(...)):
if x:
return x
# continue processing
Your latest comment maybe makes it clearer what you want to do:
Imagine that you pass f a list and it select an item, then calls itself passing the list without the item and so on until you have no more items. The you check if the solution is feasible, if it is feasible you’ll return the solution and this needs to go all the way through the call stack, otherwise you return None. In this way you’ll explore all the problems in a topological order but you can also skip checks when you know that the previous chosen items won’t be able to create a feasible solution.
Maybe you can try using yield
instead of return
. That is, your recursive function won’t generate one solution, but will yield all possible solutions. Without a specific example I can’t be sure what you’re doing exactly, but say before it was like this:
def solve(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return None
if not args:
#valid and done
return result_so_far
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
result = solve(new_args, accumulate_result(result_so_far, item)
if result is not None:
#found it, we are done
return result
#otherwise keep going
Now it looks like this:
def solve_all(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return
if not args:
#yield since result was good
yield result_so_far
return
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
for result in solve(new_args, accumulate_result(result_so_far, item):
yield result
The benefits are:
- You generate all answers instead of just the first one, but if you still only want one answer then you can just get the first result.
- Before you used return values both for false checks and for answers. Now you’re just only yielding when you have an answer.
Not really a recommendation, but you could abuse a list comprehension and do something along these lines:
# Note: Doesn't work in python 3.
def func():
if [value for value in (slow_function(),) if value is not None]:
return value
# continue processing...
Is there a better way to write this code in python?
result = slow_function()
if result:
return result
[...]
The function slow_function
can return a value or None
and it’s slow, so this is not feasible:
if slow_function():
return slow_function()
There is nothing wrong with the first way, but using a temporary variable seems overkill for python.
This code is pretty useful when you are solving a problem using recursive calls over f
and with local assumption, for example you select an item from a list and then check if there is a feasible solution, otherwise you have to choose another one. Something like:
def f(n):
for x in xrange(n):
result = slow_function(x):
if result:
return result
[...]
Wouldn’t it be better something more idiomatic like:
def f(n):
for x in xrange(n):
return slow_function(x) if is not None
This can be extended to check any kind of value. It would be an easy-to-read return if statement.
Additional example for code lovers
Imagine you have a list of lists of numbers:
lists = [[1,2,3],[4,5],[6,7,8],[9,10],...]
and you want to select one item for each list such that there is at most one even number in the selection. There could be a lot of lists, so trying each combination would be wasteful since you can already tell that if you start selecting [1,2,4,…] there could be no feasible solutions.
def check(selected):
even_numbers = filter(lambda n: (n % 2) == 0, selected)
return len(even_numbers) < 2
def f(lists, selected=[]):
if not lists:
return selected
for n in lists[0]:
if check(selected + [n]):
result = f(lists[1:], selected + [n])
if result:
return result
Wouldn’t it be better a syntax like:
def f(lists, selected=[]):
return selected if not lists
for n in lists[0]:
if check(selected + [n]):
return f(lists[1:], selected + [n]) if is not None
The best I’ve done so far is to turn the function in a generator of feasible solutions:
def f(lists, selected=[]):
if not lists:
yield selected
else:
for n in lists[0]:
if check(selected + [n]):
for solution in f(lists[1:], selected + [n]):
yield solution
Without knowing what else you might want to return there are a few options.
-
You could just return the result of the function,
None
or not:return slow_function()
In this, you rely on the caller knowing what to do with the
None
value, and really just shift where your logic will be. -
If you have a default value to return instead of None, you can do this:
return slow_function() or default
In this above, if
slow_function
isNone
(which is “falsy”) it will return the latter value, otherwise, ifslow_function
returns a “truthy” value it will return that. Beware, ifslow_function
can return other “falsy” values, likeFalse
,[]
or 0, those will be ignored. -
Alternatively, sometimes what you have is perfectly valid code. You want to compare against a value, and if it is value, return it. The code you have is obvious in what it does, and sometimes that is more important than the “cleverness” of the code.
As per the comments, if your code must continue running if the value is None
then the most obvious way to do it is store it as a temporary value. But, thats not a bad thing, as it reads cleanly.
- Compute a value and store it as the result
- If there is a valid result, return it.
- Otherwise, keep doing things to get a better result.
Better is usually very subjective, and I can’t see any obvious ways to improve this from a computation perspective, and as written it is very human readable which is a clear advantage. Other solutions may be shorter or cleverer, but human readability is often an over looked advantage for code.
Essentially you want to evaluate an expression and then use it twice without binding it to a local variable. The only way to do that, since we don’t have anonymous variables, is to pass it into a function. Fortunately, the control flow for whether the current function returns isn’t controlled by the functions it calls… however, exceptions do propagate up the call stack.
I wouldn’t say this is better, but you could abuse exceptions to get what you want. This should never really be used and it’s more an exercise in curiosity. The result would end up looking like this (note the use of the decorator):
def slow_function(x):
if x % 5 == 0:
return x * 200
@if_returner
def foobme(l):
for i in l:
print "Checking %s..." % (i,)
return_if(slow_function(i))
print foobme([2, 3, 4, 5, 6])
Output is:
Checking 2...
Checking 3...
Checking 4...
Checking 5...
1000
The trick is to piggy-back on exception handling, since those propagate across function calls. If you like it, here’s the implementation:
class ReturnExc(Exception):
def __init__(self, val):
self.val = val
def return_if(val):
if val is not None:
raise ReturnExc(val)
def if_returner(f):
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except ReturnExc, e:
return e.val
return wrapped
What you have written looks fine, but if you wanted to avoid multiple return statements you could do something like this:
def f():
result = slow_function()
if result is None:
[...]
result = [...]
return result
For the problem where slow_function
is operating over a loop, a generator expression would seem the way to go. In Python 3 everything here is lazy, so you get your filter for free:
f = filter(slow_function(x) for x in range(...))
In Python 2 you just need itertools:
from itertools import ifilter
f = ifilter(slow_function(x) for x in xrange(...))
Each iteration will only take place when you ask for it. If you need to continue the operation if the function returns false, then you need the False
ness as a sentinel, so your solution is fine:
def f():
for x in xrange(...):
sentinel = slow_function(x)
if sentinel:
return sentinel
# continue processing
or you can save yourself a variable by using a generator here, too:
from itertools import imap
def f():
for x in imap(slow_function, xrange(...)):
if x:
return x
# continue processing
Your latest comment maybe makes it clearer what you want to do:
Imagine that you pass f a list and it select an item, then calls itself passing the list without the item and so on until you have no more items. The you check if the solution is feasible, if it is feasible you’ll return the solution and this needs to go all the way through the call stack, otherwise you return None. In this way you’ll explore all the problems in a topological order but you can also skip checks when you know that the previous chosen items won’t be able to create a feasible solution.
Maybe you can try using yield
instead of return
. That is, your recursive function won’t generate one solution, but will yield all possible solutions. Without a specific example I can’t be sure what you’re doing exactly, but say before it was like this:
def solve(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return None
if not args:
#valid and done
return result_so_far
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
result = solve(new_args, accumulate_result(result_so_far, item)
if result is not None:
#found it, we are done
return result
#otherwise keep going
Now it looks like this:
def solve_all(args, result_so_far):
if not slow_check_is_feasible(result_so_far):
#dead-end
return
if not args:
#yield since result was good
yield result_so_far
return
for i, item in enumerate(args):
#pass list without args - slow
new_args = args[:]
del new_args[i]
for result in solve(new_args, accumulate_result(result_so_far, item):
yield result
The benefits are:
- You generate all answers instead of just the first one, but if you still only want one answer then you can just get the first result.
- Before you used return values both for false checks and for answers. Now you’re just only yielding when you have an answer.
Not really a recommendation, but you could abuse a list comprehension and do something along these lines:
# Note: Doesn't work in python 3.
def func():
if [value for value in (slow_function(),) if value is not None]:
return value
# continue processing...