4

When negative evaluation in if clause will cause a return call inside a function/method, what is more recommended in Python, to nest the if clauses or to utilise the inverse evaluation and call the function return? e.g.:

if required_condition_1:
    if required_condition_2:
        if required_condition 3:
            pass
        return 'error for condition 3'
    return 'error for condition 2'
return 'error for condition 1'

Or:

if not required_condition_1:
    # preparation...
    return 'error for condition 1'

if not required_condition_2:
    # preparation...
    return 'error for condition 2'

if not required_condition_3:
    # preparation...
    return 'error for condition 3'

# if runtime reaches this point it means that it has passed all conditions

Imagine you have to register a user, and you need various conditions to be satisfied. User will only be registered if they are all satisfied, but error messages depend upon what condition fails.

My guess is that in other circumstances, as a user mentions in the answers section, other actions could apply if a certain condition fails. Then I think I should nest the ifs. However, I will only be returning an error message, so I think the second option is preferable.

I have also thought about assertion:

try:
    assert(required_condition_1)
    try:
        assert(required_condition_2)
        # do tasks
    except AssertionError:
        # error for condition 2
except AssertionError:
    # error for condition 1

Even though I think this last way is not pretty recommendable as in treating exceptions. Also as an SO user mentions:

If the code is correct, barring Single-event upsets, hardware failures and such, no assert will ever fail. That is why the behaviour of the program to an end user must not be affected. Especially, an assert cannot fail even under exceptional programmatic conditions. It just doesn't ever happen. If it happens, the programmer should be zapped for it.

I know this might seem primarily opinion-based, but for me it is not since all languages have style guidelines that produce a more sustainable, scalable and readable environment depending on its characteristics and functionalities. I would like to know what if there is a recommended way of treating this matter inside methods and most importantly, why.

  • 4
    Based on the fact that code should have some "locality", I guess the second code fragment is more readable. But this holds in *any* language. – Willem Van Onsem Dec 25 '17 at 21:24

2 Answers2

1

Although this could be seen as opinion-based, I think that objectively the second way seems much more readable.

In your first example, someone reading your code would have to decrypt that each statement is not logically related to the one it is nested in, something that you do not want to happen. In general, nested blocks imply some kind of inherited logic.

The second example is much more neatly written and does not require much thought to realize the flow of the logic. Instead of treading deeper and deeper the more conditions you want to apply, the logical flow seems to imply that each should be satisfied regardless.

The first style has its use-cases, just not for the given task at hand: condition checking.


As for your second question, using asserts in this use-case would not be deemed appropriate. As a rule of thumb, only use assert when something which should never go wrong, but is very important for program execution, goes wrong. One of the most obvious uses is writing test cases, a function should give a certain output, and it is very bad if it does not give you that output.

Exceptions are for things you might expect to go wrong e.g. divide by zero errors, attribute errors, and the such when dealing with user input.

Ziyad Edher
  • 2,150
  • 18
  • 31
1

As Ziyad Edher answered, the second is more readable:

if not required_condition_1:
    # preparation...
    return 'error for condition 1'

if not required_condition_2:
    # preparation...
    return 'error for condition 2'

if not required_condition_3:
    # preparation...
    return 'error for condition 3'

And, if you would like to have only 1 return then:

if not required_condition_1:
    # preparation...
    _error = 'error for condition 1'

elif not required_condition_2:
    # preparation...
    _error = 'error for condition 2'

elif not required_condition_3:
    # preparation...
    _error = 'error for condition 3'

else:
    _error = None

return _error