Which code layout do you like more?

I can lay out the same function in two ways. Which one is better?

One return statement and a mutable temporary variable


def f(a, b):
temp = g(a)
if b:
temp = h(temp)
return temp

Two return statements and everything uses single assignment


def f(a, b):
if b:
return h(g(a)
else:
return g(a)

I prefer the second approach. Ever since I learned how prolog requires single assignment, I avoid updating variables. I’m not adamant about it, but in general, I try not to. It’s just a style thing though. I’ve met a lot of programmers (older ones, usually) that like only having one return statement in a function. I don’t know why they like that, but I’ve heard it from numerous people.

38 thoughts on “Which code layout do you like more?

  1. It might be interesting to see what pylint or any of the complexity tools say about it. Aesthetically, they're about the same to me. I don't know that I would give either a second thought, in context.

  2. def f(a,b):
    ….tmp = g(a)
    ….if b:
    ……..return h(tmp)
    ….return tmp
    # names are hard, and I might-just-maybe stutter g(a) instead of using a named variable, that depends on the circumstance.

  3. I prefer the first. You know at the first line you have a vaild return vaue, and if you dd more complex logic in the econd you could end up returning None if you mess up the logic.

  4. I was told many times by guys who came up in the era of structured programming that without exception, “functions have a single exit point, and they must have a single exit point. Only one return statement.”

    I'm more likely to do that out of habit now, just because it was beaten into me.

  5. I think the “single return value” is an artifact of the time when functions could be REALLY long, and you might miss an early return statement. That makes debugging hard.

    Nowadays, I feel like a functions should be short enough that you can keep track of the exit points.

    Not to mention that in approach 1, if the function g() is an intensive one (database access, satellite linkup, etc), you waste resources.

  6. The only reason I don't like the second one is that it packs too much functionality on a single line. I don't care about preferences for a “single return”. But I don't like how g(a) is called from two different places in the second version. Imagine adding logging or exception handling to this function. Which version is more modifiable?

    I prefer dgou's code.

  7. Returning in the middle of a function is bad. It is like using goto. It violates D-diagramm conformance (you can find out more about this by using Google). Using a variable has the additional benefit that it can have a meaningful name which makes it more clear *what* the function returns — for example price = … And the return price. temp in your example is a bad name anyway.

    In you case you can use Pythons relatively new ternary operator to have both advantages: Only one assignment and only one return statement at the end of the function:

    def f(a, b):
    price = h(g(a) if b else g(a)
    return price

  8. I prefer the first. However, if its a simple 5 line function then the second. Larger functions then I try to keep one return point…

    If you end up with more than 2 return points it gets ugly and confusing.

  9. <pre>
    def to_int_or_list(fpnumber, as_list=True):
    return list(int(fpnumber)) if as_list else int(fpnumber)
    </pre>

    if the funcction was doing something more obscure, then I might employ a temporary variable and use one exit point or two – whatever seemed more natural.

    – Paddy.

  10. I'm not consistent with these things. Why would you be? I go by what is more aesthetically pleasing, in your case (which by the way says nothing about mutability?) I'd go for two returns because it's more succinct somehow.

  11. I can use both, depending on the context. The difference is in the intent. Is the call to g() essential to executing f() or is it coincidentally needed for both conditions?

  12. The first approach can make for easier debugging, since you can then step through your method watching where your return value is updated, then easily override it through the debugger if you need to. It also means the function *always* returns on the same line number, so you can put a break point in immediately before it and catch the function immediately before returning. If means you can do things like

    bp 1043, retVal=None

    and let the debugger catch the time through the code you are returning None.

    Cheers,
    -Tennessee

  13. They are not the same, I mean, in this case they are but only because the functionality of g(x) is external to f(a, b). In fact f(a, b) is pretty much a wrapper for calling g(x) given a and b.

    Suppose you are writing new code, g(x) is not defined yet and once it is defined it will only be called from f(a, b), in fact g(x) is only being defined to be wrapped by f(a, b), g(x) is a helper function.

    In production, these functions will have a longer names as fubar_bloops(a, b) and fubar_bloops_helper(x) or something similar.

    This is ugly. Instead you should write g(x) inside f(a, b) because that is what you really mean, using that approach you end up with the first layout.

    Aaaaaaanyway, in this context I'd use *this* layout:

    def f(a, b):
    return h(g(a)) if b else g(a)

    or just
    f = lambda a, b: h(g(a)) if b else g(a)

  14. If you follow that too strictly, you can end up with huge, unwieldy blocks of conditionals. Suppose you have a function where you want to “fail fast”. Well, a single return point means you need to create a huge if/else. This is HUGE, and new programmers fall into this trap all the time.

    With multiple returns, we can do:

    def launch_rocket(mode):
    if mode == “test”:
    return True
    #100 lines of launch code

    With only one return, we get a 100-line conditional block, not counting any other block within it:

    def launch_rocket(mode):
    success = None
    if mode == “test”:
    return True
    else:
    #100 lines of launch code
    return result

  15. Absolutely… I try to do a single return where it makes no difference otherwise, but multiple returns where it makes the function that much clearer. Where I do multiple returns, I try to put a block of early return conditions together at the start of the function, then the body, then a single 'real' return value (if possible)

  16. I first learnt to program in the early 1990s using books, and thus methodologies, that were already old and discarded by their previous owners. All I really remember was the love of flowcharts and subtle re-enforcement of the idea that multiple return statements made code so hard to read that anyone else who saw it would meet an untimely end.

    I think modern teaching styles have moved towards a more understanding based approach that credits the student with a little intelligence. Consider the following fragments of introductory programming:

    * All functions must return a value
    * No code after a return is executed
    * a function can only return one value

    Nice nuggets of information that can be memorised by rote. So very early on one gets into the habit of putting the return at the end of a function because it has to be there and thinking nothing more of it. It took a long time for me to unlearn that habit.

  17. For me, it depends on the rest of the code in the function. Multiple obvious return statements aren't “bad”:

    def f(…):
    if param is bad:
    return None

    … do stuff …
    return answer

    but I don't like having returns sprinkled throughout the code, and especially not in loops, where it can complicate the logic of the execution flow.

  18. I on the other hand love multiple return statements in loops and –while using php– switches. I actually find that situation a lot using switches.

    In some switches I don't use break at all since all my cases end with return. Some times however I have to do additional work before getting out of the function, then it's temp variable time again.

  19. With multiple return points I find myself spelling out implicit else statement:

    def(a,b):
    __if b:
    ____return h(g(a))
    __else:
    ____return g(a)

  20. Shouldn't the function name be “price” instead? Why introduce a variable just for naming when the function name serves that purpose?

Comments are closed.