2

When a function contains multiple if statements, it seems to be a good practice to keep only one exit. In php-like language, I can use do-while(false), like this:

function a() {
    do {
        if (...) break;
        ...
        if (...) break;
        ...
    } while (FALSE);
    ...
    return;
}

how to do this in python? Although I can use while(true), if missing the trailing break, it'll be a dead loop.

while True:
    if ...: break
    ...
    if ...: break
    ...
    # oops, miss the 'break', you're dead

Is there a more pythonic way to do this?

PS: to further illustrate the one-exit:

function a() {
    if (...) { $a = 1; log($a); return $a; }
    if (...) { $a = 2; log($a); return $a; }
    if (...) { $a = 3; log($a); return $a; }
}

function a() {
    do {
        if (...) { $a = 1; break; }
        if (...) { $a = 2; break; }
        if (...) { $a = 3; break; }
    } while (FALSE);
    log($a);
    return $a;
}

hope you see the difference...

Jerry
  • 789
  • 1
  • 13
  • 31
  • 4
    i don't understand what you're gaining by using the do..while(false) construct. – yurib Jan 13 '12 at 09:58
  • 1
    I don't see why you need the while loop in your first example. If you don't fulfil any if statement, you get out of the if and return.. without the while it would be the same, right? – ezdazuzena Jan 13 '12 at 09:58
  • you need to give a better example cause now it seems like you could just use multiple `if` statements – soulcheck Jan 13 '12 at 09:59
  • @ezdazuzena what if I want to log the return value? I need to do this before every 'return', but using do-while(false), I only need to log once. that's what so-called one exit. I learned from a collegue, it's quite practical. – Jerry Jan 13 '12 at 10:05
  • @Jerry: Ok, maybe I don't get your point, but.. you enter into an `if` statement, in there you log the message and that's it. You might enter also into another `if` (omit it using `else if`) and log it as well. At the end of all you return. – ezdazuzena Jan 13 '12 at 10:08
  • 2
    Single exit functions are bad for a variety of reasons: you can never write a truly single exit function in most languages (exceptions are an alternative exit), so there's no point making the code contorted just for some false ideal. See also http://stackoverflow.com/questions/4838828 for more viewpoints on this. – Duncan Jan 13 '12 at 10:08
  • @Jerry: Again, what if no `if` statement is fulfilled? What is `a` in that case? – ezdazuzena Jan 13 '12 at 10:18
  • That multiple exit version is so much more readable anyway. The force-single exit is more often than not overcomplicated for no good reason. – TyrantWave Jan 13 '12 at 10:22
  • 1
    Why not use `elseif` in PHP instead of this strange `do..while`? You can do the same in Python with `elif` (see yurib's answer). – Dzinx Jan 13 '12 at 10:57
  • @DzinX: because then you can't break out of the normal control flow if you encounter for example an error. That's the whole point of wrapping it inside a loop in the first place. – Emil Styrke Jan 13 '12 at 11:51
  • @Emil: I believe OP's question wasn't about handling errors, but rather about multiple `if` statements and breaking after the first `if` succeeds. And that's what `elif` is for. Maybe let's wait for Jerry to comment on this. – Dzinx Jan 13 '12 at 12:22

4 Answers4

3

I think a while loop is an awkward way to achieve what you're looking for. Why not just use a local variable?

def f(x):
  result = None

  if (...): result = 1
  elif (...): result = 2
  elif (...): result = 3
  else: result = 4

  log(result)
  return result
yurib
  • 8,043
  • 3
  • 30
  • 55
  • 1
    +1 for `elif`s, but Python does not have a `switch` statement. – Dzinx Jan 13 '12 at 10:54
  • @DzinX: That was what I was talking about ;) – ezdazuzena Jan 13 '12 at 10:57
  • This does not work if you want to short-circuit out of some code, for example find a file, if found open it, if successful read from it, etc. Then you get a lot of nested ifs instead, which is hard to read. IMHO, try/finally is the way to go. – Emil Styrke Jan 13 '12 at 11:47
  • @EmilStyrke this is a suggestion for the example in the OP, for the case you're describing try/finally is probably more suitable. my point is that in any case there's a better solution than what is in my opinion the abuse of a `while` loop. – yurib Jan 13 '12 at 12:55
  • @yurib actually the situation that EmilStyrke describes is what I'm confronting with. I met with many nested if statements and multiple return. when I need to do something before every return, I got stuck. But try/finally is rightaway. Clearly the illustration in the post causes more confusion, which I appologize for. Thank you all~ – Jerry Jan 14 '12 at 02:51
3

To do this in Python I'd write like this (see Defining cleanup actions):

try:
    if ...: return
    ...
    if ...: return
    ...
finally:
    #Code to be executed on every return path (including exceptions) here
Emil Styrke
  • 1,073
  • 11
  • 19
  • If all that happens inthe finally is cleanup (closing file, committing db transaction, etc.), use a context manager and the `with` statement. – PaulMcG Jan 13 '12 at 13:47
  • I'm not aware that finally will execute even after return. What a newbie I am... This is what I'm looking for, thank you~ – Jerry Jan 14 '12 at 02:48
1

A more Pythonic way to achieve the goal you mentioned – logging the return value – is to wrap the whole function in a decorator. For example:

from functools import wraps

def print_return(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return_value = fn(*args, **kwargs)
        print return_value
        return return_value
    return wrapped


@print_return
def foo(a, b):
    return a + b
dhwthompson
  • 2,501
  • 1
  • 15
  • 11
-1

You can do with try, catch. You just need to raise an exception instead of break

Please refer to this page:

http://docs.python.org/tutorial/errors.html

batbaatar
  • 5,448
  • 2
  • 20
  • 26
  • I thought about that, too. But is it too expensive to raise an exception? – Jerry Jan 13 '12 at 10:00
  • Once, I tried to do something like this one. After lots of Googling, the final solution was this one. If you find something better, please let me know – batbaatar Jan 13 '12 at 10:06
  • @Jerry: That's the wrong question to ask, it totally depends on your use case. Benchmarking is the only way to know. However, exceptions are for exceptional cases, they should not be used for flow control like that. – Emil Styrke Jan 13 '12 at 10:24
  • A better way is to use try/finally and regular return or break to exit. – Emil Styrke Jan 13 '12 at 10:35
  • Try-catch should only be used for exceptional conditions, not flow control. – erturne Jan 13 '12 at 11:07
  • Well, at least it saves time if your data is large. I don't think there would be any expensive operations to catch an exception. You can see couple of examples above using try, catch and finally. – batbaatar Jan 13 '12 at 14:46