1

The return early pattern is an alternative to nested if/else statements. In Ruby this is sometimes see as one line:

# Ruby
x = f(y) or return APPROPRIATE_ERROR

It will return if the expression on the left of or is falsey.

Is there a Python way of doing this?, other than

# Python
x = f(y)
if not x:
    return APPROPRIATE_ERROR
michael_teter
  • 1,880
  • 2
  • 15
  • 14
  • 2
    The [`:=`](https://stackoverflow.com/questions/26000198/what-does-colon-equal-in-python-mean) operator is probably worth looking at. – Chris Apr 05 '22 at 22:35
  • 1
    I think walrus and if-guarded return is your best bet here. You can write it in one line if you disregard pep8. I believe everything else to allow this would be quite hacky, see e.g. this approach https://stackoverflow.com/questions/18669836/is-it-possible-to-write-single-line-return-statement-with-if-statement – Banana Apr 05 '22 at 22:41
  • I see @Chris, this could be shortened to two lines with `:=`. – michael_teter Apr 05 '22 at 22:44
  • Thanks @Banana, I see. `if (x := f(y)) is None: return APPROPRIATE_ERROR`. But indeed, that would probably anger the people who have fully embraced Black... – michael_teter Apr 05 '22 at 22:48

3 Answers3

2

With the := operator, your Python example can be made a little more concise.

if not (x := f(y)):
    return APPROPRIATE_ERROR

After this, assuming the condition wasn't met, you can use x as normal.

There are a number of places where this becomes useful. I find it especially useful in writing lambdas, where I can't do a normal assignment but don't want to recalculate a value repeatedly.

Chris
  • 26,361
  • 5
  • 21
  • 42
  • 1
    This is the best solution which will hopefully not cause many style disagreements; I did mark the other one-line answer as *the answer* since it technically is. – michael_teter Apr 05 '22 at 23:15
2

It's syntactically correct to do

...
if not (x := f(y)): return APPROPRIATE_ERROR
...

Generally, it's not recommended as standard style, but the option is there if you like it

Kaia
  • 862
  • 5
  • 21
  • 1
    This is technically the correct answer to my original question. Unfortunately, as pointed out by Banana, this goes against current Python style guides. So perhaps for those free to choose for themselves, this is the solution. Otherwise, the two-line Chris answer is what is the best ("approved") alternative. – michael_teter Apr 05 '22 at 23:13
1

Im adding this just for fun (and to elaborate on my comment). You can take the hacky decorator approach from the second answer from here. For completeness:

class ReturnValue(Exception):
    def __init__(self, value):
        Exception.__init__(self)
        self.value = value


def enable_ret(func):
    def decorated_func(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except ReturnValue as exc:
            return exc.value

    return decorated_func


def ret(value):
    raise ReturnValue(value)

Now, remember that if and while blocks are exceptions in the scoping rules. Specifically, variables defined within an if or while block are available in the scope of the function. If I understood your question correctly, you could now use this like so:

@enable_ret
def testfunc(x):
    # Return None if y is false
    ret(None) if not (y := x) else 0

    # Otherwise we continue (and can access y) here
    print(y)
    return 1

Please dont do this though :)

Banana
  • 1,149
  • 7
  • 24