It depends. A little.
For trivial cases, I'll often keep an else for obvious parallelism:
def foo(arg1, arg2):
if condition:
return arg1
else:
return arg2
But for anything that gets remotely complicated, I'll drop the else, and generally advise others do the same. This is because it's really difficult to follow a function like this:
def foo():
if cond1:
# ten lines here
return value
else:
if cond2:
# another ten lines
return value
else:
if cond3:
# ten more lines
return value
# ...a couple dozen lines here...
return value
Say I want to refactor this function, or some code that uses it, and I want to know how exactly the return value is computed because someone forgot to write documentation. The first thing I see is three separate ifs all with a bunch of code in them, and now it looks like there are eight different combinations of things that might have happened to value, and who knows how many paths to each return and I sigh and take a swig of vodka and go to figure out what they all are.
Worse, imagine if each of those if blocks only conditionally does a return.
On the other hand, this is pretty easy to follow even with many more conditions:
def foo():
if cond1:
# ten lines here
return value
if cond2:
# another ten lines
return value
if cond3:
# ten more lines
return value
# ...a couple dozen lines here...
return value
How many ways can this go? Four, and they're clearly intended to be mutually exclusive, and the returns even line up visually.
Nested blocks read like exponential complexity. Sequential blocks read like a series of steps, which is more like what you intend to convey.
Ultimately I think keeping your returns outdented as far as possible is a good compromise with the spirit of the Single Return Principle, which I've otherwise never bothered to follow.
And yes, I've refactored code like this :)