## What you'll learn

- You recognize that certain sub-problems, such as reading input or calculations, recur in programs.
- You're aware of solution models to certain sub-problems.
- You practice combining solution patterns used on sub-problems to solve broader ones.

The same small problems, or "sub-problems", reappear in programs time after time: "Read input from the user", "Calculate the sum of values", and so forth.

Positive : Note that from now on, we might drop the if `__name__ == '__main__':` block from the code snippets. You should assume that it still exists, and write it in the exercises for the code you submit.

Let's look at a few sub-problems and patterns for solving them.

## Reading User Input

The solution pattern for programming tasks involving reading user input is straightforward. If the program needs to calculate the square root of a function, we need to import some code which has been created for the task. We will use the library `numpy` for this, which is imported at the top of the example below. Importing the tool makes it available for the program. Importing external libraries is an important part of programming, and allows us to not ‘reinvent the wheel'. We'll cover importing modules in more detail a later part of the course.

    # Making the numpy library available in the program
    import numpy as np

    def sqrt():
        number = 9
        sqrt_number = np.sqrt(number)
        print(sqrt_number)

## Calculating

We quite often need to calculate something in a program, such as an average or a sum. The solution pattern to solve such problems is as follows.

1. Define the inputs required for the calculation and declare variables for them. Input refers to the values used in the calculation. You can typically identify the type of inputs from the problem description.
2. Identify the operation needed, and declare a variable for the result of the calculation. Perform the calculation using the inputs, and assign the result to the variable that was reserved for it. The type of the result can also usually be identified from the problem description.
3. Once the calculation is done, do something with its result. This can mean printing the result of a computation, or, for example, using it in calculating an average by dividing a sum of the inputs by their count.

For example, the solution pattern for the problem Create a program to calculate the sum of two integers is the following.

    # Identifying the input values and declaring the variables for them
    first = 1
    second = 2

    # Identifying the operation and declaring a variable for the result
    sum = first + second

    # printing the result of the calculation
    print("The sum of " + str(first) + " and " + str(second) + " is " + str(sum))

A program that both reads and calculates combines both of these patterns. One that calculates the product of two integers provided by the user looks like this:

    def product():
        # Assigning the user input to the variables
        first = int(input())
        second = int(input())

        # Identifying the operation and declaring a variable for the result
        product = first * second

        # Printing the result of the operation
        print("The product of " + str(first) + " and " + str(second) + " is " + str(product))

In the example above, the program has been implemented so that the variables are declared and values are read into them.

Positive : Exercise - Squared
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Square Root of Sum
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## Conditional Logic

Problems often contain some conditional logic. In these instances we use conditional statements. A conditional statement starts with an `if` command followed by an expression in parentheses. The expression evaluates to either true or false. If it evaluates true, the following block delimited by curly brackets gets executed.

    # if the value is greater than five
    if (value > 5):
        # then...

A program that prints "ok" if the value of the variable is greater than `42`, and otherwise prints "not ok" looks like this:

    value = 15
    if (value > 42):
        print("ok")
    else:
        print("not ok")

You can also chain together multiple conditions. In such a case, the problem takes the form "if a, then b elif c, then d elif e, then f otherwise g". The chain consists of an `if`-statement followed by `elif`-statements, each containing its own expression and a block.

    # if the value is greater than five
    if (value > 5):
        # functionality when value is greater than five
    elif (value < 0): #
        # functionality when value is less than zero
        # and the value IS NOT larger than five
    else:  # otherwise
        # functionality otherwise

Conditional logic can be combined with other patterns used for problem solving. Let's look into a problem "Read two integers from the user. If the product of the integers is over 100, print `too much`. If the sum is less than 10, print `too little`. Otherwise, print `ok`. The program below combines reading, calculating and conditional functionality.

    def product():
        # Assigning the user input to the variables
        first = int(input())
        second = int(input())

        # Identifying the operation and declaring a variable for the result
        product = first * second

        # Printing the result of the operation
        if (product > 100):
            print('too much')
        elif (product < 10):
            print('too little')
        else:
            print('ok')

Positive : Exercise - Absolute Value
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Comparing Numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## What you'll learn

- You are familiar with loops and know how to create a program that contains one.
- You know how to use the `break` command to end a loop's execution and move onto its next statement.
- You know how to use `continue` command to return to the beginning of a loop.
- You are able to create a program that reads inputs from a user until a specific input is given. For example, the number 0 or the string "end", after which the program prints something about the provided inputs (e.g., the input count, and in the case of numbers their sum and average).

A computer's processor, which specializes in executing commands, is capable of executing – in a modern computer – over a billion (machine code) commands in a second.

In this section, we'll get used to writing often-repeated program code with the help of loops.

Let's motivate ourselves to use loops. Below you'll find an example of a program that asks the user for five numbers and calculates their sum.

    sum = 0

    print("Input a number: ")
    sum = sum + int(input())

    print("Input a number: ")
    sum = sum + int(input())

    print("Input a number: ")
    sum = sum + int(input())

    print("Input a number: ")
    sum = sum + int(input())

    print("Input a number: ")
    sum = sum + int(input())

    print("The sum of the numbers is " + str(sum))

It does the job, but not elegantly. What if the program had to read a hundred, or perhaps a thousand numbers and print their sum? What if the program had to read three numbers only?

The problem can be solved with a loop which keeps track of both the sum and the number of times input has been read. The program that prints the sum of five numbers now looks as follows

    numbers_read = 0
    sum = 0

    while True:
        if (numbers_read == 5):
            break

        sum = sum + int(input("Input number"))
        numbers_read = numbers_read + 1

    print("The sum of the numbers is " + str(sum))

Next off we will get familiar with loops.

## Loops and Infinite Loops

A loop consists of a statement that determines whether or not the code within the loop should be repeated, along with a block containing the source code to be repeated. A loop takes the following form.

    while (_statement_):
        # The content of the block
        # The block can have an unlimited amount of content

We'll use the value `True` as the loop's statement for now. This way, the loop's execution is always continued when the program arrives at the point that decides whether it should be repeated or not. This happens when the execution of the program first arrives at the loop statement for the first time, and also when it reaches the end of the loop's block.

The loop execution proceeds line-by-line. The following program outputs I can program an infinite number of times.

    while True:
        print("I can program!")

A program that runs infinitely does not end on its own. In Python, it can usually be shut down by the Ctrl-C command.

## Ending a Loop

The loop statement can be broken out of with command ‘break'. When a computer executes the command ‘break', the program execution moves onto the next command following the loop block.

The example below is a program that prints numbers from one to five. Note how the variable that's used within the loop is defined before the loop. This way the variable can be incremented inside the loop and the change sticks between multiple iterations of the loop.

    number = 1

    while True:
        print(number)
        if (number >= 5):
            break

        number = number + 1

    print("Ready!")

Negative : 1
2
3
4
5
Ready!

Breaking out of the loop occurs when a user enters a specified input or whenever a calculation performed in the loop ends in the desired result. These kinds of programs contain both a loop used to define a section to be repeated and also a conditional statement used to check whether or not the condition to exit the loop has been fulfilled.

Users can also be asked for input within a loop. The variables that are commonly used in loops are defined before the loop, whereas variables (such as the value read from the user) that are specific to the loop are defined within it.

In the example below, the program asks the user whether to exit the loop or not. If the user inputs the string "y", the execution of the program moves to the command following the loop block, after which the execution of the program ends.

    while True:
        print("Exit? (y exits)")
        message = input()
        if (message == "y"):
            break

        print("Ok! Let's carry on!")

    print("Ready!")

The program in the example works as follows. The user's inputs are marked with User:.

Negative : Exit? (y exits)
User: <no>
Ok! Let's carry on!
Exit? (y exits)
User: <non>
Ok! Let's carry on!
Exit? (y exits)
User: <y>
Ready!

Positive : Exercise - Carry on?
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

In the previous example, the program read inputs of type string from the user. The program can also be implemented with other types of variables. The program below asks numbers from the user until the user inputs a zero.

    while True:
        number = int(input("Input a number, 0 to quit"))
        if (number == 0):
            break

        print("You input " + str(number))

    print("Done, thank you!")

The output of the program can be as follows:

Negative : Input a number, 0 to quit
User: <5>
You input 5
Input a number, 0 to quit
User: <-2>
You input -2
Input a number, 0 to quit
User: <y>
Done, thank you!

Positive : Exercise - Are we there yet?
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## Returning to the Start of the Loop

When the execution reaches the end of the loop, the execution starts again from the start of the loop. This means that all the commands in the loop have been executed. You can also return to the beginning from other places besides the end with the command `continue`. When the computer executes the command `continue`, the execution of the program moves to the beginning of the loop.

The example below demonstrates the use of the `continue` command. The program asks the user to input positive numbers. If the user inputs a negative number or a zero, the program prints the message "Invalid number! Try again", after which the execution returns to the beginning of the loop. In the previous example, the program read inputs of type string from the user. Similar programs with different input types are also possible. In the example below, the user is asked for numbers until they input a zero.

    while True:
        number = int(input("Insert positive integers"))

        if (number <= 0):
            print("Invalid number! Try again.")
            continue

        print("Your input was " + str(number))

The program in the example above is repeated infinitely since the `break` command used for exiting the loop is not used. To exit the loop, the `break` command must be added to it.

In the example below, the program is modified in such a way that the user is asked to input positive numbers. If the user inputs a negative number, the program informs them that the number was invalid and returns to the beginning of the loop. If the number was zero, the program exits the loop.

    while True:
        number = int(input("Insert positive integers"))

        if (number == 0):
            break

        if (number <= 0):
            print("Invalid number! Try again.")
            continue

        print("Your input was " + str(number))

Positive : Exercise - Only positives
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

In the previous exercise, you made a program that asks the user for numbers. If the user entered a negative number, the program would inform them that the number was invalid, and if the user entered a zero, the program would exit. A possible solution to the exercise is the following.

    while True:
        number = int(input("Input a number"))

        if (number == 0):
            break

        if (number <= 0):
            print("Invalid number! Try again.")
            continue

        print(number * number)

The program could be made by modifying the if-statement to another form. In the example below, the conditionals have been combined to replace separate if-statements.

    while True:
        number = int(input("Input a number"))

        if (number == 0):
            break
        elif (number < 0):
            print("Invalid number")
            continue

        print(number * number)

Which of the previous examples was more clear?

Let's examine the clarity of the previous programs through an example. Below, the program asks the user for a number. If the number is negative, the user is informed that the number is invalid and the execution of the program goes to the beginning of the loop. If the number is zero, the program exits the loop. In other cases the program prints the square of the number, i.e., the number times itself.

    while True:
        number = int(input("Input a number"))

        if (number < 0):
            print("Invalid number")
            continue

        if (number == 0):
            break

        print(number * number)

This program can also be done by combining the if-statements. In that case, the implementations would be the following.

    while True:
        number = int(input("Input a number"))

        if (number < 0):
            print("Invalid number")
        elif (number == 0):
            break
        else:
            print(number * number)

Let's examine the previous programs with comments. Before each command, there's a comment that aims to explain what's happening in the program. Below is a program that's written with separate if-statements.

    # The task is to repeat the block until the block is exited
    while True:
        # The task is to ask the user for an input and read a number from the user
        number = int(input("Input a number"))

        # The task is to guard and prevent invalid numbers
        # for further processing
        if (number < 0):
            print("Invalid number")
            continue

        # The task is to check if the loop should be exited
        if (number == 0):
            break

        # The task is to print the square of the number
        print(number * number)

Note that every if-statement has a single, clear task.

When we comment on a program containing combined if-statements, the comments take the following form.

    # The task is to repeat the block until the block is exited
    while True:
        # The task is to ask the user for an input and read a number from the user
        number = int(input("Input a number"))

        # The purpose of the if-elif-else block?
        # The task is the processing of the number?
        if (number < 0):
            print("Invalid number")
        elif (number == 0):
            break
        else:
            print(number * number)

We notice that it's difficult to define a single, clear task for the `if-elif-else`-block. During the design and implementation of a program, it's desirable to aim for a situation in which every part of the program has a single, clear task. This theme repeats throughout the course.

## Calculation with Loops

Loops are used in computing many different things. For example, programs that process indefinite numbers of user-inputted values make use of loops. These kinds of programs typically print out some sort of statistics about the numbers that were read or other inputs after the end loop.

For the program to print out information from the loop execution after the loop, the information must be saved and modified during the loop.

If the variable used to store the data is introduced within the loop, the variable is only available within that loop and nowhere else.

Let's create a program to count and print out the number of ones entered by the user. Let's first create a non-working version and examine the action of the blocks.

    # The task is to read an input from the user
    while True:

        # The task is to keep count of number ones
        ones = 0

        # The task is to ask the user for an input and read a number from the user
        number = int(input("Input a number (0 exits)"))

        # The task is to exit the loop if the user
        # has inputted zero
        if (number == 0):
            break

        # The task is to increase the amount of ones
        # if the user inputs a number one
        if (number == 1):
            ones = ones + 1 #can also achieve the same thing by ones += 1

    # The task is to print out the total of ones
    # This doesn't work because the variable ones has been
    # introduced within the loop
    print("The total of ones: " + str(ones))

The previous program does not work because the variable `ones` is introduced within the loop, and an attempt is made to use it after the loop at the end of the program. The variable only exists inside the loop. If the print statement `print("The total of ones: " + ones)` was inside the loop, the program would work, but not in the desired way. Let's examine this next.

    # The task is to read an input from the user
    while True:

        # The task is to keep count of number ones
        ones = 0

        # The task is to ask the user for an input and read a number from the user
        number = int(input("Input a number (0 exits)"))

        # The task is to exit the loop if the user
        # has inputted zero
        if (number == 0):
            break

        # The task is to increase the amount of ones
        # if the user inputs a number one
        if (number == 1):
            ones = ones + 1

        # The task is to print out the total of ones
        print("The total of ones: " + str(ones))

The example above works, but not in a way we hoped it would. Below the example output of the program

Negative : Insert a number (0 exits)
User: <5>
The total of ones: 0
Insert a number (0 exits)
User: <1>
The total of ones: 1
Insert a number (0 exits)
User: <1>
The total of ones: 1
Insert a number (0 exits)
User: <2>
The total of ones: 0
Insert a number (0 exits)
User: <0>

If you wish to use a variable after a loop, it needs to be introduced before the loop.

In the example below, the program computes the total of number ones inputted. The inputs are read until the user inputs a zero after which the program prints the total count of number ones entered. The program uses variable `ones` to keep track of the number ones.

    # The task is to keep track of number ones
    ones = 0

    # The task is to read an input from the user
    while True:
        # The task is to ask the user for an input and read a number from the user
        number = int(input("Input a number (0 exits)"))

        # The task is to exit the loop if the user
        # has inputted zero
        if (number == 0):
            break

        # The task is to increase the amount of ones
        # if the user inputs a number one
        if (number == 1):
            ones = ones + 1

    # The task is to print out the total of ones
    print("The total of ones: " + str(ones))

Below is an example output of the program.

Negative : Insert a number (0 exits)
User: <1>
Insert a number (0 exits)
User: <2>
Insert a number (0 exits)
User: <1>
Insert a number (0 exits)
User: <-1>
Insert a number (0 exits)
User: <0>
Total of ones: 2

Positive : Exercise - Number of Numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Number of Negative Numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

The programs written in the previous exercises have read input from the user and kept track of the count of certain types of numbers. In the next exercise, the requested sum of numbers is not much different – this time, rather than keeping track of the number of values entered, you add the number entered by the user to the sum.

Positive : Exercise - Sum of Numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Sometimes you need to use multiple variables. The example below shows a program which reads numbers from the user until the user writes 0. Then the program prints the number of positive and negative numbers given, and the percentage of positive numbers from all numbers given.

    # For saving number of numbers
    number_of_positives = 0
    number_of_negatives = 0

    # For repeatedly asking for numbers
    while True:
        # The task is to ask the user for an input and read a number from the user
        number_from_user = int(input("Input a number (0 exits)"))

        # For breaking the loop when user writes 0
        if (number_from_user == 0):
            break

        # For increasing number_of_positives by one
        # when user gives a positive number
        if (number_from_user > 0):
            number_of_positives = number_of_positives + 1

        # For increasing number_of_negatives by one
        # when user gives a negative number
        if (number_from_user < 0):
            number_of_negatives = number_of_negatives + 1

        # Also could have used:
        # if (number_from_user > 0):
        #     number_of_positives = number_of_positives + 1
        # else:
        #     number_of_negatives = number_of_negatives + 1
        #

    # For printing the number of positive numbers
    print("Positive numbers: " + str(number_of_positives))
    # For printing the number of negative numbers
    print("Negative numbers: " + str(numberOfNegative))

    # For printing the percentage of positive numbers from all numbers
    if (number_of_positives + number_of_negatives > 0):
        # Print only if user has given numbers
        # to avoid dividing by zero
        percentageOfPositives = 100.0 * number_of_positives / (number_of_positives + number_of_negatives)
        print("Percentage of positive numbers: " + str(percentageOfPositives) + "%")

Positive : Exercise - Number and sum of numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Average of numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Average of positive numbers
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## What you'll learn

- You're familiar with the condition of the while loop.
- You know how to use the for loop.
- You recognize situations where a while loop should be used and those where a for loop is more appropriate.

The "while-true" loop we've been using is very handy when the program has to repeat a functionality until the user provides certain input.

Next, we'll come to know a few other ways to implement loops.

## While Loop with a Condition

So far we have been using a loop with the boolean `True` in its parenthesis, meaning the loop continues forever (or until the loop is ended with the `break` command ).

Actually, the parenthesis of a loop can contain a conditional expression, or a condition, just like the parenthesis of an `if` statement. The `True` value can be replaced with an expression, which is evaluated as the program is executed. The expression is defined the same way as the condition of a conditional statement.

The following code prints the numbers 1,2,...,5. When the value of the variable `number` is more than 5, the `while`-condition evaluates to false and the execution of the loop ends for good.

    number = 1

    while (number < 6):
        print(number)
        number+= 1

The code above can be read "As long as the value of the variable number is less than 6, print the value of the variable number and increase the value of the variable number by one".

Above, the value of the variable `number` is increased by one every time the loop body is executed.

## For Loop

Above, we learned how a `while` loop with a condition can be used to go through numbers in a certain interval.

The structure of this kind of loop is the following.

    i = 0
    while (i < 10):
        print(i)
        i += 1

The above loop can be split into three parts. First we introduce the variable `i`, used to count the number of times the loop has been executed so far, and set its value to 0: `i = 0` . This is followed by the definition of the loop – the loop's condition is `i < 10` so the loop is executed as long as the value of the variable `i` is less than 10. The loop body contains the functionality to be executed `print(i)` , which is followed by increasing the value of the variable `i += 1`. The command `i += 1` is shorthand for `i = i + 1`.

The same can be achieved with a `for` loop like so.

    for i in range(1,10):
        print(i)

A `for` loop contains three parts:

- introducing the index variable `i` for counting the number of executions
- the condition of the loop
- the functionality to be executed.

    for target_list "in" expression_list:
        # Functionality to be executed

We will continue practicing loops in the following exercises. You can use either a `while` loop with a condition, or a `for` loop.

Positive : Exercise - Counting
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Counting to a hundred
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - From where to where?
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## On Stopping a Loop Execution

A loop does not stop executing immediately when its condition evaluates to true. A loop's condition is evaluated at the start of a loop, meaning when (1) the loop starts for the first time or (2) the execution of a previous iteration of the loop body has just finished.

Let's look at the following loop.

    number = 1

    while (number != 2):
        print(number)
        number = 2
        print(number)
        number = 1

It prints the following:

Negative : 1
2
1
2
1
2
...

Even though `number` equals 2 at one point, the loop runs forever.

The condition of a loop is evaluated when the execution of a loop starts and when the execution of the loop body has reached the closing curly bracket. If the condition evaluates to `True`, execution continues from the top of the loop body. If the condition evaluates to `False`, execution continues from the first statement following the loop.

## Repeating Functionality

One common subproblem type is to "do something a certain amount of times".

What's common to all these programs is repetition. Some functionality is done repeatedly, and a counter variable is used to keep track of the repetitions.

The following program calculates the product 4*3 somewhat clumsily, i.e., as the sum 3 + 3 + 3 + 3:

    result = 0

    i = 0
    while True:
        result += 3  # shorthand for result = result + 3
        i += 1   # shorthand for i = i + 1

        if (i == 4):
            break

    print(result)

The same functionality can be achieved with the following code.

    result = 0

    i = 0
    while (i < 4):
        result += 3  # shorthand for result = result + 3
        i += 1   # shorthand for i = i + 1

    print(result)

Or by using a for loop as seen in the following.

    result = 0

    for i in range(4):
        result += 3

    print(result)

Positive : Exercise - Sum of a sequence
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Sum of a sequence - the sequel
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Factorial
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## On the Structure of Programs Using Loops

In the previous examples, we have concentrated on cases where the loop is executed a predetermined number of times. The number of repetitions can be based on user input – in these cases, the for loop is quite handy.

In programs where the loop body has to be executed until the user gives certain input, the for loop is not too great. In these cases, the while-true loop we practiced earlier works well.

Let's take a look at a somewhat more complex program that reads integers from the user. The program handles negative numbers as invalid, and zero stops the loop. When the user enters zero, the program prints the sum of valid numbers, the number of valid numbers and the number of invalid numbers.

A possible solution is detailed below. However, the style of the example is not ideal.

    print("Write numbers, negative numbers are invalid: ")
    sum = 0
    valid_numbers = 0
    invalid_numbers = 0

    while True:
        message = int(input())

        if (message == 0):
            print("Sum of valid numbers: " + str(sum))
            print("Valid numbers: " + str(valid_numbers))
            print("Invalid numbers: " + str(invalid_numbers))
            break

        if (message < 0):
            invalid_numbers+= 1
            continue

        sum += message
        valid_numbers+= 1

In the code above, the computation executed after the loop has ended has been implemented inside of the loop. This approach is not recommended as it can easily lead to very complex program structure. If something else – for example, reading more input – is to be done when the loop ends, it could also easily end up being placed inside of the loop. As more and more functionality is needed, the program becomes increasingly harder to read.

Let's stick to the following loop structure:

    # Create variables needed for the loop

    while True:
        # read input

        # end the loop -- break

        # check for invalid input -- continue

        # handle valid input

    # functionality to execute after the loop ends

In other words, the program structure is cleaner if the things to be done after the loop ends are placed outside of it.

    print("Write numbers, negative numbers are invalid: ")
    sum = 0
    valid_numbers = 0
    invalid_numbers = 0

    while True:
        message = int(input())

        if (message == 0):
            break

        if (message < 0):
            invalid_numbers+= 1
            continue

        sum += message
        valid_numbers+= 1

    print("Sum of valid numbers: " + str(sum))
    print("Valid numbers: " + str(valid_numbers))
    print("Invalid numbers: " + str(invalid_numbers))

Positive : Exercise - Repeating, breaking and remembering
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Implementing a program one small part at a time
In the previous exercise, we used a series of exercises to practice implementing a program one piece at a time.
When you are writing a program, whether it's an exercise or a personal project, figure out the types of parts the program needs to function and proceed by implementing them one part at a time. Make sure to test the program right after implementing each part.
Never try solving the whole problem at once, because that makes running and testing the program in the middle of the problem-solving process difficult. Start with something easy that you know you can do. When one part works, you can move on to the next.
Some of the exercises are already split into parts. However, it's often the case in programming that these parts need to be split into even smaller parts. You should almost always run the program after every new line of code. This ensures that the solution is moving in the right direction.

## What you'll learn

- You will be familiar with the concept of a class.
- You are familiar with the concepts of a method parameter, a method's return value, and a program's call stack.
- You can create parameterized and non-parameterized methods, and you can create methods that return a value.

So far, we've used various commands: value assignment, calculations, conditional statements, and loops.

Printing to the screen has been done with the statement `print()`, and the reading of values with `input()`. `if` has been used in conditional statements, and `while` and `for` in loops. We notice that printing and reading operations somewhat differ from `if`, `while`, and `for` in that the print and read commands are followed by parentheses, which may include parameters passed to the command. The ones that "end in parentheses" are not actually commands, but methods.

Technically speaking, a method is a named set of statements. It's a piece of a program that can be called from elsewhere in the code by the name given to the method. For instance `print("I am a parameter given to the method!")` calls a method that performs printing to the screen. The internal implementation of the method – meaning the set of statements to be executed – is hidden, and the programmer does not need to concern themselves with it when using the method.

So far all the methods we have used have been ready-made Python methods. We will learn to create our own methods in this section, but first let's revisit `if __name__ == '__main__':`, the directory structure and importing packages.

## Directories, pacakges and __name__ == ‘__main__'

You've probably noticed by now that the exercise files contain a specific directory structure that looks as follows:

    exercise-files/
    +-- README.md
    +-- src/
    |   +-- __init__.py
    |   +-- exercise.py
    +-- tests/
    |   +-- test_exercise.py

Some of these files should be fairly self explanatory. The automated testing is run from the `tests/` folder. The `README.md` file explains what the exercise is about, and READMEs in general explain what the code in the repository is about.

The `src/` directory contains all of the source code for the project. Since we've only had one Python file until now, there are no more folders inside `src`. The `__init__.py` file (which has been empty for us so far), is required to make Python treat the directories as containing packages which can then be imported. If you look at one of the testing files:

    import pytest
    from src.exercise import average # this imports the method 'average' from the 'exercise.py' file in the 'src' folder

    def test_exercise():
        assert average(16, 12, 5, 3) == 9 # this runs the 'average' method and checks if the answer is correct

you can see that our package is imported from the `src` folder and used in the program. This is common Python functionality. You have previously imported and used the `numpy` package which allowed you to use a `sqrt` function. This is exactly the same functionality, except that you haven't written the `numpy` package. Python knows where to get `numpy` from because it is a common package listed in the Python Package Index (PyPI) , which is a repository of software for the Python programming language. You can create your own packages and make them available on PyPi if you think they would be useful for others.

What is name == ‘main'?

So far, our `exercise.py` files have looked like this:

    def main():
        # some code ...

    if __name__ == '__main__':
        main()

Whenever the Python interpreter reads a source file, it does two things:

- it sets a few special variables, such as `__name__`, and then
- it executes all of the code found in the file.

When the Python interpeter reads a source file, it first defines a few special variables. In this case, we care about the `__name__` variable.

When Your Module Is the Main Program

If you are running your module (the source file) as the main program, e.g.

    python exercise.py

the interpreter will assign the hard-coded string `"__main__"` to the `__name__` variable, i.e.

    # It's as if the interpreter inserts this at the top
    # of your module when run as the main program.
    __name__ = "__main__"

When Your Module Is Imported By Another

On the other hand, suppose some other module is the main program and it imports your module. This means there's a statement like this in the main program, or in some other module the main program imports:

    # Suppose this is in some other main program.
    import exercise

The interpreter will search for your `exercise.py` file (along with searching for a few other variants), and prior to executing that module, it will assign the name "exercise" from the import statement to the `__name__` variable, i.e.

    # It's as if the interpreter inserts this at the top
    # of your module when it's imported from another module.
    __name__ = "exercise"

In this case, the `if __name__ == '__main__':` statement would resolve to `False` and anything within the block wouldn't run.

Why does it work this way?

You might naturally wonder why anybody would want this. Well, sometimes you want to write a `.py` file that can be both used by other programs and/or modules as a module, and can also be run as the main program itself. Examples:

- Your module is a library, but you want to have a script mode where it runs some unit tests or a demo.
- Your module is only used as a main program, but it has some unit tests, and the testing framework works by importing `.py` files like your script and running special test functions. You don't want it to try running the script just because it's importing the module.
- Your module is mostly used as a main program, but it also provides a programmer-friendly API for advanced users.

Beyond those examples, it's elegant that running a script in Python is just setting up a few magic variables and importing the script. "Running" the script is a side effect of importing the script's module.

For us, it's important to have this statement for the testing routines. As you learn more about modules, packages and libraries, you'll come to understand this more. Don't worry too much about it for now. If you are interested you can check out this thread, from which this was adapted.

## Custom Methods

A method means a named set consisting of statements that can be called from elsewhere in the program code by its name. Programming languages offer pre-made methods, but programmers can also write their own ones. It would, in fact, be quite exceptional if a program used no methods written by the programmer, because methods help in structuring the program. From this point onward nearly every program on the course will therefore contain custom-created methods.

We will also introduce the concept of a class here, which will become far clearer as we go through the course. A Class is like an object constructor, or a "blueprint" for creating objects.

To create a class, use the keyword `class`:

    class MyClass:
      x = 5

Now we can use the class named MyClass to create objects. Create an object named p1, and print the value of x:

    class MyClass:
      x = 5

    p1 = MyClass()
    print(p1.x)

Negative : 5

Note that according to the Python PEP 8 styling guidelines, the names of classes should normally use the CapWords convention. This means that the first letter is capitalised and the first letter of every word in the class name is capitalised.

The examples above are classes and objects in their simplest form, and are not really useful in real life applications.

To understand the meaning of classes we have to understand the built-in `__init__()` function.

All classes have a function called `__init__()`, which is always executed when the class is being initiated.

We can use the `__init__()` function to assign values to object properties, or other operations that are necessary to do when the object is being created.

In the code boilerplate, methods are written outside of the function `__init__()`, yet inside out the "outermost" class block. They can be located above or below the `__init__()`.

    class Example:
        def __init__(self, *args):
            # program code

        # your own methods here

This can be confusing at first, but the concept of a class, including what the keywords `self` and `*args` mean will be clearer later in the course. For now, let's turn our attention back to methods an observe how to create a new method. We'll create the method `greet`.

    def greet(self):
        print("Greetings from the method world!")

Note the subtle difference between a method and a function. Until now, we have used functions such as

    def add():
      a = 2
      b = 3
      print(a+b)

This can become a method when we insert it into a suitable place in a class.

    class Example:
        def __init__(self, *args):
            # program code

        # your own methods here
        def greet(self):
            print("Greetings from the method world!")

Note also the keyword `self` here. This means that the method is associated with a class and can access variables within that class. Methods always have `self` as an input parameter. We will revisit the concept of `self` many times throughout the course.

The definition of the method consists of two parts. The first line of the definition includes the name of the method, i.e. `greet`. Beneath the line containing the name of the method is a code block, inside of which is the code of the method – the commands that are executed when the method is called. The only thing our method `greet` does is write a line of text on the screen.

Calling a custom method is simple: write the name of the methods followed by a set of parentheses. In the following snippet the main function (main) calls the greet method four times in total.

    class Example:
        def __init__(self, *args):
            # program code
            print("Let's see if we can travel to the method world:")
            self.greet()

            print("Looks like we can, let's try again:")
            self.greet()
            self.greet()
            self.greet()

        # your own methods here
        def greet(self):
            print("Greetings from the method world!")

    def main():
        my_class = Example()

    if __name__ == '__main__':
        main()

The execution of the program produces the following output:

Negative : Let's see if we can travel to the method world:
Greetings from the method world!
Looks like we can, let's try again:
Greetings from the method world!
Greetings from the method world!
Greetings from the method world!

The order of execution is worth noticing. The execution of the program happens by executing the lines of the main function (`main`) in order from top to bottom, one at a time. When the encountered statement is a method call, the execution of the program moves inside the method in question. The statements of the method are executed one at a time from top to bottom. After this the execution returns to the place where the method call occurred, and then proceeds to the next statement in the program.

When the program starts, the operating system calls `main`. The main function is the starting point for the program, since the execution begins from its first line. The execution of a program ends at the end of the main function.

Positive : Methods vs Functions
A method in python is somewhat similar to a function, except it is associated with object/classes. Methods in python are very similar to functions except for two major differences.
1. The method is implicitly used for an object for which it is called.
2. The method is accessible to data that is contained within the class.
You can read more here.

Positive : Exercise - In a hole in the ground
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Reprint
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

From here on out, when introducing methods, we will not explicitly mention that they must be located in the correct place. Methods cannot be defined e.g. inside other methods.

## On Naming Methods

According to the Python PEP 8 styling guidelines, the names of methods use the function and variable naming rules and therefore should be entirely lowercase with words separated by underscores as necessary to improve readability.

In the code example below the method is poorly named. It begins with an upper-case letter and the words are incorrectly separated. The parentheses after the method name have a space between and indentation in the code block is incorrect (2 spaces instead of 4).

    def Thismethod_says_woof ( self):
      print("woof")

In contrast the method below is correctly named: The name is entirely lowercase with words separated by underscores as necessary to improve readability. The parentheses sit next to one another and the contents are correctly indented (the method has its own code block, so the indentation of the code is four characters).

    def this_method_says_woof(self):
        print("woof")

## Method Parameters

Parameters are values given to a method that can be used in its execution. The parameters of a method are defined on the uppermost line of the method within the parentheses following its name. The values of the parameters that the method can use are copied from the values given to the method when it is executed.

In the following example a parameterized method `greet` is defined. It has a parameter called `num_of_times`.

    def greet(self, num_of_times):
        i = 0
        while (i < num_of_times):
            print("Greetings!")
            i += 1

We will call the method `greet` with different values. The parameter `num_of_times` is assigned the value `1` on the first call, and `3` on the second.

    def __init__(self):
        self.greet(1)
        print("")
        self.greet(3)

Negative : Greetings!
Greetings!
Greetings!
Greetings!

Just like when calling the predefined method `print`, you can pass an expression as a parameter.

    def __init__(self):
        self.greet(1 + 2)

Negative : Greetings!
Greetings!
Greetings!

If an expression is used as a parameter for a method, the expression is evaluated prior to the method call. Above, the expression evaluates to `3` and the final method call is of the form `greet(3)` .

Positive : Exercise - From one to parameter
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - From parameter to one
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## Multiple Parameters

A method can be defined with multiple parameters. When calling such a method, the parameters are passed in the same order.

    def sum(self, first, second):
        print("The sum of numbers " + str(first) + " and " + str(second) + " is " + str(first + second))

    sum(3, 5)

    number1 = 2
    number2 = 4

    sum(number1, number2)

Negative : The sum of numbers 3 and 5 is 8
The sum of numbers 2 and 4 is 6

Positive : Exercise - Division
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## Parameter Values Are Copied in a Method Call

As a method is called the values of its parameters are copied. In practice, this means that both the main method and the method to be called can use variables with the same name. However, changing the value of the variables inside the method does not affect the value of the variable in the main method that has the same name. Let's examine this behavior with the following program.

    class Example:
        def __init__(self):
            min = 5
            max = 10

            self.print_numbers(min, max)
            print()

            min = 8

            self.print_numbers(min, max)

        def print_numbers(self, min, max):
            while (min < max):
                print(min)
                min += 1

    def main():
        my_class = Example()

The output of the program is:

Negative : 5
6
7
8
9
8
9

So, method parameters are distinct from the variables (or parameters) of other methods, even if they had the same name. As a variable is passed to a method during a method call, the value of that variable gets copied to be used as the value of the parameter variable declared in the method definition. Variables in two separate methods are independent of one another.

To further demonstrate this point, let's consider the following example. We define a variable called `number` in the main method. That variable is passed as a parameter to the method `increment_by_three`.

    # main program
    def __init__(self):
        number = 1
        print("The value of the variable 'number' in the main program: " + str(number))
        self.increment_by_three(number)
        print("The value of the variable 'number' in the main program: " + str(number))

    # method
    def increment_by_three(self, number):
        print("The value of the method parameter 'number': " + str(number))
        number = number + 3
        print("The value of the method parameter 'number': " + str(number))

The execution of the program produces the following output.

Negative : The value of the variable ‘number' in the main program: 1
The value of the method parameter ‘number': 1
The value of the method parameter ‘number': 4
The value of the variable ‘number' in the main program: 1

When the variable `number` is incremented inside the method, there's no issue. This, however, is not reflected in the `number` variable of the main program. The `number` variable living in the main program is different from the `number` variable of the method. The parameter `number` is copied for the method's use, i.e., a new variable called `number` is created for `increment_by_three` method, to which the value of the variable`number` in the main program is copied during the method call. The variable `number` inside the method `increment_by_three` exists only for the duration of the method's execution and has no relation to the variable of the same name in the main program.

## Methods & Functions Can Return Values

To return a value, we use the command `return` followed by the value to be returned (or the name of the variable whose value is to be returned).

    def always_returns_ten(self):
        return 10

The method defined above returns a value of `10` when called. For the return value to be used, it must be stored in a variable. This is done the same way as regular value assignment to a variable, by using an equals sign.

    def __init__(self):
        number = self.always_returns_ten()

        print("the method returned the number " + str(number))

The return value can also be used in any other expression.

    number = 4 * self.always_returns_ten() + (self.always_returns_ten() / 2) - 8

    print("the result of the calculation " + str(number))

Positive : Exercise - Numero uno
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

When execution inside a method reaches the command `return`, the execution of that method ends and the value is returned to the calling method. Any lines of source code following the command `return` are never executed.

    def functioning_method(self, parameter):
        if (parameter > 10):
            return 10

        print("The number received as parameter is ten or less.")

        return parameter

        print("This is never reached.")

If a method has the form `def name_of_method()` it is possible to return from it – in other words, to stop its execution in that place – with the `return` command that is not followed by a value. For instance:

    def division(self, numerator, denominator):
        if (denominator == 0):
            print("Can not divide by 0!")
            return

        print("" + str(numerator) + " / " + str(denominator) + " = " + str(1.0 * numerator / denominator))

## Defining Variables Inside Methods

Defining variables inside methods is done in the same manner as in the "main program". The following method calculates the average of the numbers it receives as parameters. Variables `sum` and `avg` are used to help in the calculation.

    def average(self, number1, number2, number3):
        sum = number1 + number2 + number3
        avg = sum / 3.0

        return avg

Variables defined in a method are only visible inside that method. In the example above, this means that the variables `sum` and `avg` defined inside the method `average` are not visible in the main program. A typical mistake while learning programming is to try and use a method in the following way.

    def __init__(self):
        first = 3
        second = 8
        third = 4

        self.average(first, second, third)

        # trying to use a method's internal variable, DOES NOT WORK!
        print("The average of the numbers: " + str(avg))

In the above example, an attempt is made to use the variable `avg` that has been defined inside the method `average` and print its value. However, the variable `avg` only exists inside the method `average`, and it cannot be accessed outside of it.

The following mistakes are also commonplace.

    def __init__(self):
        first = 3
        second = 8
        third = 4

        # trying to use the method name only, DOES NOT WORK!
        print("The average of the numbers: " + average)

Above, there is an attempt to use the name of the method `average` as if it were a variable. However, a method has to be called.

As well as placing the method result into a helper variable, another way that works is to execute the method call directly inside the print statement:

    def __init__(self):
        first = 3
        second = 8
        third = 4

        # calling the method inside the print statement, DOES WORK!
        print("The average of the numbers: " + str(self.average(first, second, third)))

Here, the method call occurs first returning the value 5.0, which is then printed with the help of the print statement.

## Calculating the Return Value Inside a Method

The return value does not need to be entirely pre-defined - it can also be calculated. The return command that returns a value from the method can also be given an expression that is evaluated before the value is returned.

In the following example, we'll define a method sum that adds the values of two variables and returns their sum. The values of the variables to be summed are received as method parameters.

    def sum(first, second):
        return first + second

When the execution of the method reaches the statement `return first + second` , the expression `first + second` is evaluated, and then its value is returned.

The method is called in the following way. Below, the method is used to add the numbers 2 and 7 together. The value resulting from the method call is placed into the variable `sum_of_numbers`.

    sum_of_numbers = sum(2, 7)
    # sum_of_numbers is now 9

Let's expand the previous example so that the numbers are entered by a user.

    def __init__(self):
        first = int(input("Enter the first number: "))

        second = int(input("Enter the second number: "))

        print("The combined sum of the numbers is: " + str(self.sum(first, second)))

    def sum(self, first, second):
        return first + second

In the example above, the method's return value is not stored in a variable but is instead directly used as part of the print operation. The print command's execution is done by the computer first evaluating the string `"The combined sum of the numbers is: "+ self.sum(first, second)`. The computer first looks for the variables `first` and `second` and copies their values as the values ​​of the method `sum`'s parameters. The method then adds the values of the parameters ​​together, after which it returns a value. This value takes the place of the `sum` method call, whereby the sum is appended to the string `"The combined sum of the numbers is: "`.

Since the values passed to a method are copied to its parameters, the names of the parameters and the names of the variables defined on the side of the caller have, in fact, nothing to do with each other. In the previous example, both the variables of the main program and the method parameters were named the same (`first` and `second`) "by accident". The code below will function in precisely the same manner even though the variables are named differently:

    def __init__(self):
        number1 = int(input("Enter the first number: "))

        number2 = int(input("Enter the second number: "))

        print("The total sum of the numbers is: " + self.sum(number1, number2))

    def sum(self,first,second):
        return first + second

Now the value of the variable `number1` is copied as the value of the method parameter `first`, and the value of the variable `number2` is copied as the value of the parameter `second`.

Positive : Exercise - Summation
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Smallest
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Greatest
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

Positive : Exercise - Averaging
Read the instructions for the exercise and commit the solution via Github.
Accept exercise on Github Classroom

## Execution of Method Calls and the Call Stack

How does the computer remember where to return after the execution of a method?

The environment that executes Python source code keeps track of the method being executed in the call stack. The call stack contains frames, each of which includes information about a specific method's internal variables and their values. When a method is called, a new frame containing its variables is created in the call stack. When the execution of a method ends, the frame relating to a method is removed from the call stack, which leads to execution resuming at the previous method of the stack.

When a method is called, the execution of the calling method is left waiting for the execution of the called method to end. This can be visualized with the help of a call stack. The call stack refers to the stack formed by the method calls – the method currently being executed is always on the top of the stack, and when that method has finished executing the execution moves on to the method that is next on the stack. Let's examine the following program:

    def __init__(self):
        print("Hello world!")
        self.print_number()
        print("Bye bye world!")

    def print_number(self):
        print("Number")

The execution begins from the first line of the `__init__` method when the program is run. The command on this line prints the text `"Hello world!"`. The call stack of the program looks as follows:

Negative : __init__

Once the print command has been executed, we move on to the next command, which calls the method `print_number`. Calling this method moves the execution of the program to the beginning of the method `print_number`. Meanwhile, the `main` method will await for the execution of the method `print_number` to end. While inside the method `print_number`, the call stack looks like this:

Negative : print_number
__init__

Once the method `print_number` completes, we return to the method that is immediately below the method `print_number` in the call stack – which in this case is the method `main`. `print_number` is removed from the call stack, and the execution continues from the line after the `print_number` method call in the `main` method. The state of the call stack is now the following:

Negative : __init__

## Call Stack and Method Parameters

Let's examine the call stack in a situation where parameters have been defined for the method.

    def __init__(self):
        beginning = 1
        end = 5

        self.print_stars(beginning, end)

    def print_stars(self, beginning, end):
        while (beginning < end):
            print("*")
            beginning += 1  # same as beginning = beginning + 1

The execution of the program begins on the first line of the `main` method. The next two lines create the variables `beginning` and `end`, and also assign values to them. The state of the program prior to calling the method `print_stars`:

Negative : __init__
* beginning = 1
* end = 5

When `print_stars` is called, the `__init__` method enters a waiting state. The method call causes new variables `beginning` and `end` to be created for the method `print_stars`, to which the values passed as parameters are assigned to. These values are copied from the variables `beginning` and `end` of the `__init__` method. The state of the program on the first line of the execution of the method `print_stars` is illustrated below.

Negative : print_stars
* beginning = 1
* end = 5
__init__
* beginning = 1
* end = 5

When the command `beginning+= 1` is executed within the loop, the value of the variable `beginning` that belongs to the method currently being executed changes.

Negative : print_stars
* beginning = 2
* end = 5
__init__
* beginning = 1
* end = 5

As such, the values of the variables in the method `__init__` remain unchanged. The execution of the method `print_stars` would continue for some time after this. When the execution of that method ends, the execution resumes inside the `__init__` method.

Negative : __init__
* beginning = 1
* end = 5

## Call Stack and Returning a Value from a Method

Let's now study an example where the method returns a value. The `__init__` method of the program calls a separate `start` method, inside of which two variables are created, the `sum` method is called, and the the value returned by the `sum` method is printed.

    def __init__(self):
        self.start()

    def start(self):
        first = 5
        second = 6

        sum = self.sum(first, second)

        print("Sum: " + str(sum))

    def sum(self,number1, number2):
        return number1 + number2

At the beginning of the `start` method's execution the call stack looks as in the following illustration since it was called from the `main` method. The method `main` has no variables of its own in this example:

Negative : start
__init__

When the variables `first` and `second` have been created in the `start` method (i.e., the first two rows of that method have been executed), the situation is the following:

Negative : start
* first = 5
* second = 6
__init__

The command `sum = sum(first, second)` creates the variable `sum` in the method `start` and calls the method `sum`. The method `start` enters a waiting state. Since the parameters `number1` and `number2` are defined in the method `sum`, they are created right at the beginning of the method's execution, after which the values of the variables given as parameters are copied into them.

Negative : sum
* number1 = 5
* number2 = 6
start
* first = 5
* second = 6
* sum # no value
__init__

The execution of the method `sum` adds together the values of the variables `number1` and `number2`. The command `return` returns the sum of the numbers to the method that is one beneath it in the call stack - the method `start` in this case. The returned value is set as the value of the variable `sum`.

Negative : start
* first = 5
* second = 6
* sum = 11
__init__

After that, the print command is executed, and then we return to the `__init__` method. Once the execution reaches the end of the `__init__` method, the execution of the program ends.

## Method Calling Another Method

As we noticed earlier, other methods can be called from within methods. An additional example of this technique is given below. We'll create the method `multiplication_table` that prints the multiplication table of the given number. The multiplication table prints the rows with the help of the `print_multiplication_table_row` method.

    def multiplication_table(self,max):
        number = 1

        while (number <= max):
            self.print_multiplication_table_row(number, max)
            number+= 1

    def print_multiplication_table_row(self, number, coefficient):

        printable = number
        while (printable <= number * coefficient):
            print("  " str(printable))
            printable += number

        print("")

The output of the method call `multiplication_table(3)`, for instance, looks like this.

Negative : 1
2
3
2
4
6
3
6
9

Programs grow in size as we add more functionality to them. When the program size increases, so does the complexity. This results in source code that is more difficult to understand. In this part, we took our first steps towards managing program complexity: loop structures can be used to execute program code time and again and custom methods allow for dividing a program into smaller, more manageable parts.