As written in the other posts there is no “should”.
In general it is a question of the operations you want to perform.
When you have just two operations (operation when conditions are met and operation when not) it makes most sense to have a single condition expression.
if conditionA and conditionB:
operationWhenConditionsMet()
When you think the condition is too complex you can encapsulate it into a function.
if areConditionsMet():
operationWhenConditionsMet()
...
def areConditionsMet():
return conditionA and conditionB
This increases readability quite a lot especially when the name of the function tells what the expression is meant to be.
As more nested conditions you have as more complex the code is.
Example:
if conditionA:
if conditionB:
operationWhenConditionMet()
is more complex than:
if conditionA and conditionB:
operationWhenConditionMet()
You see there is one indentation level less (indentation levels are one indicator to measure complexity).
In opposite to two operations when you have more operations you have to check many conditions in combination:
if conditionA:
if conditionB:
operationWhenConditionAandBMet()
else:
operationWhenConditionAandNotBMet()
else:
if conditionB:
operationWhenConditionNotAandBMet()
else:
operationWhenConditionNotAandNotMet()
The alternative transfers complexity from indentation to operations per line eventually repeating conditions:
if conditionA and conditionB:
operationWhenConditionAandBMet()
elif conditionA and not conditionB:
operationWhenConditionAandNotBMet()
elif not conditionA and conditionB:
operationWhenConditionNotAandBMet()
elif not conditionA and not conditionB:
operationWhenConditionNotAandNotMet()
Completeness: At the first code block it is easier to see that the conditions are complete, while the second has a potential “else”. Logically it will never met the else, but you need to analyse all the conditions first.
Efficiency: When executing the code the first code block checks each condition exactly once = 2 checks. The second in worst case checks both conditions four times = 8 checks (best case = 2 checks).
Maintanance: The first code block is easier to refactor to reduce complexity:
if conditionA:
operationWhenConditionAMet()
else:
operationWhenConditionNotA()
...
def operationWhenConditionAMet():
if conditionB:
operationWhenConditionAandBMet()
else:
operationWhenConditionAandNotBMet(
def operationWhenConditionNotA():
if conditionB:
operationWhenConditionNotAandBMet()
else:
operationWhenConditionNotAandNotMet()
[now imagine you introduce a conditionC]