when to use "ifs" and when to use "ands"

When should I use many ifs like this:

if timer == 0:
    if sensor.positive:
        if boolean == False:
            stuff

and when should I use ands like this:


if timer == 0 and sensors.positive and boolean == False:
    stuff

I know there could be times when you need stuff in between ifs like:

if timer == 0:
    print('stuff')
    if sensor.positive:
        print('more stuff')
        if boolean == False:
            print('even more stuff')

but lets say I was writing a script where I didn’t need stuff in between ifs, is there still any reason why i should use one method over the other?

I don’t see any performance reason why to use one over the other, but in general not throwing hadouken’s everywhere is a good idea.


This is semantics rather than syntax. In other words: python doesn’t care, so it’s up to you.

Generally I group things by category, so with a light bulb:


if plugged_in and switched_on:
    if bulb_not_broken:
        glow()

Or maybe for a weapon of some kind:


if trigger_pulled:
    if reload_time and bullets_in_magazine:

Also, in python, you don’t have to compare to try or false:


if plugged_in == False:

is exactly the same as:


if not plugged_in:

@elmeunick9: That is an awesome picture!

BTW don’t use if x == False. Equality comparisons between a boolean and another type returns a boolean anyway (x == False resolves to False if the boolean of x is true, otherwalse false, so x==False becomes either True or False, meaning you can just write “if not x”)

OK, thanks guys!

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]

I guess I’ve been thinking of scripting wrong, I think of it like there is a way you “should” do something and a way you should not do something, and when I am scripting I am always wondering if I am doing it right, or if there is a better way to do it, even if the code still works.

There are lots of ways of coding, you find little useful tricks as you get more experience.

One of my favorites is to set a default variable just befor a script.

door_locked = true

Then do some ifs to see if it changes:


if player.alive:
    if left.click in mouse_events:
        if mouse_over.object==Door:
            if player.has_door_key("red"):
                door_locked=False

Now I don’t have to do any else setting because the default exists already.

I like to put triggers at the front of the if chain and checks at the end. This means that if the fast performinv trigger fails, the slow performing check won’t get run.

You can also put all those checks together in a single check linked with ands, there’s no performance difference sincd the first failed check aborts the others. But python style suggests avoiding long lines, so you’d have to split it over several lines anyway, losing readability and gaining nothing.

I also prefer to put checks in their own functions like player.has_door_key() above, as this makes the code more readable and makes writing the code easier.

OK, thanks!

One instance where nesting is probably good practice is when something might not exist.


player = None

if (player['name'] == "John" and player != None):
    print("something")

Will throw an error, because you cannot access a subscript of nothing.

This can be solved by putting the condition if player != None first so that it breaks before reaching the next condition.


player = None

if (player != None and player['name'] == "John"):
    print("something")

In my opinion, this makes the code difficult to understand, because if player != None dictates whether the entire block will run or not, and we are shoving it in the same line as code attempting to access it. Nesting makes the code easier to read:


player = None

if (player != None):
    if (player['name'] == "John"):
        print("Something")

@ MrPutuLips:
Good point. You can easily get an error if you mix up the order of the checks.

BTW: You don’t need the () around the statement, and you can also do “if player” rather than “if player != None”. Although you might want to do some other kind of check because player could be [“trash”] and still pass that first check.


One reason people put everything on one line is for readability, but if you use an IDE you’ll probably notice that lines shouldn’t be too long (again for readability). Good formatting keeps lines short by spreading statements across multiple lines, split at the comma and indented.

PEP-8 specifies 79 characters as the limit

If you’re going to do that anyway then it’s probably better to nest your statements as it helps keep things clear.

@MrPutuLips

if player and player['name'] == "John": print("") 

I prefer to use one-lines when possible.

(EDIT: I know you already know this, but I write it anyway for those who may not)

Note that on any language one of the first things you should learn (though unfortunatelly it’s usually skiped) is the operators precedence, chaining order and lazyness, see python documentation on the matter: https://docs.python.org/3/reference/expressions.html#operator-precedence

From de documentation:

Note that comparisons, […] have a left-to-right chaining feature […]

Since you are only using “and/or” operators, wich are on the same level of precedence, and you know thta python uses left-to-right order and lazy evaluation you know that doing:

if fA() and fB(): print("Hello")

and

if fA():
    if fB(): print("Hello")

Are exactly the same thing. fB() will be ignored in both examples if fA() returns false.

All of this was just to explain why doing “if d and d[v]” doesn’t throw and exception, and that is becouse if “d” is false, you ignore “d[v]”. Note that in python both “None” also evaluates to “false” as Smoking_mirror said, reason why you can do “if d” instead of “if d != None”, (0 also evaluates to false, in fact it may be more acurate to say that false evaluates to 0 but whatever).

Now as a last thing, as Smoking_mirror also commented, you may want to put a try…catch there for when your key is not in the container (you’ll get a KeyError), you can also try doing “if d and v in d and d[v]” (<– what I usually do, feel free to criticise) or “if d and d.get(v, None)” (what you should actually do).