Why I always assign intermediate values to local variables instead of passing them directly to function calls

Instead of

def do_something(a, b, c):
    return res_fn(
        fn(a, b),
        fn(b),
        c
    )

I do:

def do_something(a, b, c):
    inter_1 = fn(a, b)
    inter_2 = fn(b)

    result = res_fn(inter_1, inter_2, c)
    return result

The first version is much shorter, and when formatted properly, equally readable.

But the reason I prefer the second approach is because all intermediate steps are saved to local variables.

Exception tracking tools like Sentry, and even Django's error page that pops up when DEBUG=True is set, capture the local context. On top of that, if you ever have to step through the function with a debugger, you can see the exact return value before stepping out from the function. This is the reason why I even save the final result in a local variable, just before returning it.

At the performance cost of couple of extra variable assignments, and couple of extra lines of code, this makes debugging much easier.