<jupyter_start><jupyter_text>Boolean conditionsAnother **class** that is available in Python is the **boolean**. It is used to represent if a condition is verified or not.A **boolean** can only have 2 possible values: `True` or `False`.These are possible values that can be assigned to variables and used in your Python code, such as you have done with numbers (**int**, **float**) and text (**string**);<jupyter_code>x = True
y = False
x = y

# Boolean can also be printed
print(y)<jupyter_output><empty_output><jupyter_text>**Boolean** values are written capitalized, i.e. with the first letter uppercase.If you write them differently, the Python program will not execute and you will get an error.Writing `true` or `false` in your program, will result in having Python treat them as **variable** names.Your program will generate an error if you try to use them as, hopefully, you have not defined **variables** with such names.The Python editor will help you spot this error by the fact that correctly spelled **boolean variables** are displayed in bold green text.Try to execute the next code block.<jupyter_code>a = true<jupyter_output><empty_output><jupyter_text>Comparison operatorsPython does not only provides mathematical operators, but it also provides comparison operators.In the general case (i.e. let's exclude mathematical operations on **strings** for a while) a mathematical operator represents an expression between numbers and produces a number as a result.On the other hand, a comparison operator represents an expression between numbers and produces a **boolean object** as a result.Here some examples of comparison operators.<jupyter_code># If 2 is greater than 5, then `2 > 5` will evaluate to True, otherwise it will evaluate to False
# The produced boolean value is then assigned to the variable `x`
x = 2 > 5
print("x is:", x)

a = 3
y = a <= 5 # <= is the smaller or equal operator
print("y is:", y)

b = 4
z = b == 1 # == is the equality operator: is one object equivalent to another?
k = a != b # != is the inequality operator: is one object different from another?
print("z is:", z, "k is:", k)<jupyter_output><empty_output><jupyter_text>**Note the difference between the equality operator `==` and the assignement operator `=`**Expressions like the ones above behave exactly as probably more familiar ones like `x = 2 + 3`. You perform a computation and you assign the result to a **variable**.  ExercisePrint the requested values<jupyter_code>a = "Hello"
## Perform equality comparison between variable `a` and "World" and print the result

b = 7
## Perform a greater or equal comparison between 10 and variable `b` and print the result<jupyter_output><empty_output><jupyter_text>Logical operatorsPython also provides some particular comparison operators that are written in plain English: the logical operators.Logical operators are tipically used when multiple conditions have to be combined.Differently from comparison operators which always act on two numeric **objects**, logical operators acts on one or two **boolean objects**. - `and`: it's True if both the boolean **objects** are True - `or`: it's True if at least one boolean **objects** is True - `not`: it's True only if the boolean **objects** is FalseAs you can see, `and` and `or` operates on two objects, while `not` operates on a single one.The following tables are called truth tables and contain the results of logical operations.You will need to learn them in order to use **boleans** in your program.| x | y | x and y ||:---:|:---:|:---:|| `True` | `True` | `True` || `True` | `False` | `False` || `False` | `True` | `False` || `False` | `False` | `False` |    | x | y | x or y ||:---:|:---:|:---:|| `True` | `True` | `True` || `True` | `False` | `True` || `False` | `True` | `True` || `False` | `False` | `False` ||x | not x ||:---:|:---:|| `True` | `False` || `False` | `True` |<jupyter_code>a = 5
b = a > 1 and a < 10
print("Is a between 1 and 10?", b)<jupyter_output><empty_output><jupyter_text>Try to predict what the following code block will produce as output, then execute it to confirm your prediction.If your prediction was wrong, try to add some print statements throughout the code, in order to show the value of all the involved **variables**.<jupyter_code>x = 2 > 5 and 3 > 1.1
y = 3 > 1 and 8 <= 3

z = 0.01 > 2 * 0.001 or 8 > 11 or 3 == 4
k = False or 10 > 1

j = False and False

m = not y and not k
p = (x and y) or (j and not m)

print("p is:", p)<jupyter_output><empty_output><jupyter_text>`if` statementA **statement** in Python is a way for controlling the flow of execution of the code.In all the Python programs that you have seen so far, all the instructions were executed one after the other.The `if` statement allows to check a condition and to change the behavior of the program accordingly.An example consists in running a block of code only if a condition is verified.<jupyter_code># This is an `if` statement
if 2 > 5:
    print("I'm printing this line because 2 is greater than 5")

print("This line is not part of an if statement, so it's always executed")

# This is another `if` statement
c = 8
if c <= 10:
    print("I'm printing this line because c is smaller or equal than 10")
    
print("Another line that is not part of an if statement")<jupyter_output><empty_output><jupyter_text>As you may notice an `if` **statement** is made of the following parts: - the `if` keyword detenos the beginning of the **statement** - a **boolean** condition that will be evaluated by the program - the `:` at the end of that line - one or more line of code that constitute the body of the `if` statement. These are the lines that are conditionally executed. You can clearly identify them because they are indented 4 spaces with respect to the rest of the code.Indentation is the process of adding leading whitespaces at the beginning of a line of code.Indentation is always done with multiple of 4 spaces.Consecutive lines of code with the same amount of indentation, belong to the same "block of code".**NOTE** You can indent code pressing the `TAB` button on your keyboard (i.e. the one at the left of letter `Q`).  AlternativesAs you have seen in the previous example, using the `if` **statement** allows to executes some lines of the code only when some conditions are verified.The rest of the code is always executed.The `if` **statements** can become more complex than that, for example you can specify what to do if a condition is verified and an alternative to be executed only when the condition is not verified.The next code cell will print a certain message if some conditions are verified.The optional `else` clause allows to specify an alternative body if the condtions are not met.When it's present, the `else` acts as a default (catch all) condition.P.S. note that the body of an `if` **statement** can be made of any number of lines, not just 1 per case. All the lines must be properly indented.<jupyter_code>a = 3
print("This is always executed and a is", a)

if a == 4 or a > 8:
    print("a is a beautiful number")
else:
    print("hey")
    print("a is an ugly number")
    
print("This is always executed")<jupyter_output><empty_output><jupyter_text>ExerciseWrite a program in order to check if a number is even or odd. Test it on both input numbers.Use `print()` to see if the program behaves as expected.Hint: the percentage symbol `%` is called **modulo operator**.An operation between two numbers using this operator will produce as result the remainder of an integer division betwen the two numbers.    x = 10 % 6You can check if a number is even if its remainder of a division by 2 is 0.<jupyter_code># Input numbers
x = 3
y = 62

## Write your if statements here<jupyter_output><empty_output><jupyter_text>A lot of conditionsNote that an `if` **statement** can be made of any number of conditions.The first condition must always be indicated with the keyword `if`.Then you can add as many additional conditions as you want using the keyword `elif` (which translates to "else if" in plain English).Lastly, you can eventually add the `else` keyword: this has no **boolean** condition associated to it and its body is executed whenever none of the previous conditions was verified.Note that each `if` **statement** must have exactly 1 `if` (at the beginning) and it can have at maximum 1 `else` (at the end), while there are no limits on the number of `elif`.The order of the conditions in an `if` statement is very important! After a condition is verified, all the following ones are automatically ignored.<jupyter_code>x = 10

# This is the first if statement
if x == 1:
    print(x, "is equal to 1")
elif x == 2:
    print(x, "is equal to 2")
elif x < 3:
    print(x, "is smaller than 3")
elif x >= 4 and x < 8:
    print(x, "is greater or equal than 4 and smaller than 8")
else:
    print(x, "is none of the above")

# This is the second if statement
if x < 6:
    print(x, "is smaller than 6")
elif x == 7:
    print(x, "is equal to 7")

# This is the third if statement
if x > 20:
    print(x, "is greater than 20")
elif x > 8:
    print(x, "is greater than 8")
elif x > 3:
    print(x, "is greater than 3")
else:
    print(x, "is probably just a number")<jupyter_output><empty_output><jupyter_text>Look at the indentation and remeber that there can only be 1 `if` in each **statement** to clearly understand which lines are part of which block.Remember that at maximum 1 case of an `if` **statement** will be executed.<jupyter_code>if 3 > 2:
    print("This is the first statement")
if 3 > 1:
    print("This is the second statement")
if 3 > 2:
    print("This is the 1st case in the third statement")
elif 3 > 1:
    print("This is the 2nd case in the third statement")<jupyter_output><empty_output><jupyter_text>How to use `if` statements effectivelyThe `if` **statement** allows to conditionally execute parts of the code, depending on the value of some control **variables**.You may have the following situations: - A piece of code has to be executed only when a condition is verified, while all the rest of the code has to be always executed. In this case you will use only the `if` clause and no `elif` or `else`. - One among different alternative pieces of code has to be executed. In this case it's fundamental to also have the `else` clause to make sure that one of the alternatives will always be executed. Depending on the number of alternatives you may or may not have `elif` clauses. - One among different alternative pieces of code has to be executed only when some conditions are verified, while all the rest of the code will always be executed. In this case you will have one or more `elif` clauses, depending on the number of alternatives, but no `else` clause because when none of the conditions is verified none of the alternatives has to be executed.As you can see, the scenarios described before are characterized by the following elements: the number of alternatives and the presence or not of a default behavior.<jupyter_code>x = 10
if x < 5:
    print("This block is executed only if x is less than 5")
    x = 6

print("This block is always executed")
print("x is:", x)

if x == 6:
    print("This is a possible alternative, x is 6")
    x = 7
elif x > 10:
    print("This is a possible alternative, x is greater than 10")
    x = 8
else:
    print("This is the default alternative, for all x different from 6 that are not greater than 10")
    x = 9

print("This block is always executed")
print("x is:", x)

if x > 10:
    print("This block is executed only if x is greater than 10")
    x = x + 1
elif x > 8:
    print("This block is executed only if x is greater than 8 but not greater than 10")
    x = x * 2
    
print("This block is always executed")
print("x is:", x)<jupyter_output><empty_output><jupyter_text>CountersA counter is a fundamental concept in programming.It is an **int** **variable** that, as its name says, is used to count events.Remember that using the assignment operator you can update the value of a **variable**, e.g. incrementing it by 1 whenever something happens.Don't forget to first initialize the counter variable with a value (generally `0`) otherwise you will get an error when trying to update it the first time (i.e. you are using an undefined variable in an assignment).<jupyter_code># This is the counter variable
x = 0

x = x + 1
print("Count", x)

x = x + 1
print("Count", x)

x = x + 1
print("Count", x)

print("In total, I counted", x)<jupyter_output><empty_output><jupyter_text>A counter is a very simple concept and it has no particular rules, it's just a **variable** as any other.Moreover, after looking at the previous example, you may be asking yourself what's the purpose of that code.After all, as the author of the code, when writing the previous example, you already knew that the counter would have been incremented 3 times.The truth is that counters are useful when used in combination with Python **statements**. Remeber that a statement allows to control the flow of execution.An `if` **statement** allows us to update the counter only when a condition is verified.Consider as an example a digital clock where the displayed numbers change every minute. The code inside the digital clock could be considered as if it's checking the condition "is 1 minute passed since my last counter update?" and then act accordingly.A possible use case for you is to use a counter to keep track of how many times a condition was verified in a block of code, given a particular input.<jupyter_code># This is the counter variable
c = 0

x = 5
if x < 12:
    c = c + 1

x = 8
if x < 12:
    c = c + 1

x = x * 2 * 3
if x < 12:
    c = c + 1
    
if c >= 2:
    print("2 or more conditions have been verified")<jupyter_output><empty_output><jupyter_text>Nested conditionsYou can write an `if` **statement** within another `if` **statement**.The body of an `if` **statement** is just a generic block of code as all the rest, with the difference that is executed only if its preconditions are satisfied.<jupyter_code>n = 0

# This is the first if statement
if not 2 > 5:
    n = n + 1

# This is the second if statement
if n != 0 or n == 0:
    n = n + 1
    # This is the third if statement
    if 8.5 <= 5.8:
        n = n + 1
    elif 7.7 > 7.7 and 2.2 == 2.2:
        n = n + 1
    else:
        n = n - 1
else:
    n = n + 1

# This is the fourth if statement
if n == 2:
    n = n + 1
    # This is the fifth if statement
    if n > 5:
        n = n + 1
    elif n < 2:
        n = n + 1
    else:
        n = n + 1
else:
    n = n + 2

print(n)<jupyter_output><empty_output><jupyter_text>ExerciseComplete the task using what you learnt about `if` **statements**.<jupyter_code># Input data
a = 10
b = 11

# Write some code that: 
# - prints a message if the sum of the input values is between 10 and 30
# - prints a message if at least one of the input values is greater than 10
# - prints how many of the above conditions were verified<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08556756035747633, 'fraction_numerical': 0.013405362144857944, 'mean_word_length': 4.01672800267648, 'pattern_counts': {'":': 0, '<': 79, '<?xml version=': 0, '>': 91, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '67840', 'license': 'MIT', 'n_tokens_mistral': 4330, 'n_tokens_neox': 4082, 'n_words': 2335, 'path': '03_conditions/boolean.ipynb', 'repo_name': 'alsora/python3-course-notebooks', 'size': '14994'}