author: Scott Morgan summary: The second part of the material focusses on repetition in programs and how to divide functionality into distinct units. id: part-2 categories: python-programming environments: Web status: Published feedback link: https://github.com/Scott3142/python-programming analytics account: UA-49751789-4
Duration: 01:00:00
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.
__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.
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)
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.
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.
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')
Duration: 01:00:00
break
command to end a loop’s execution and move onto its next statement.continue
command to return to the beginning of a loop.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.
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.
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!")
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:.
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:
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))
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.
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
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.
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.
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) + "%")
Duration: 01:00:00
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.
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.
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:
i
for counting the number of executionsfor 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.
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:
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.
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)
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))
Duration: 01:00:00
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.
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:
__name__
, and thenWhen 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:
.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.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.
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)
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:
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.
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.
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")
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)
Just like when calling the predefined method print
, you can pass an expression as a parameter.
def __init__(self):
self.greet(1 + 2)
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)
.
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)
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:
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.
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 variablenumber
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.
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))
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 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.
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
.
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:
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:
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:
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
:
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.
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.
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.
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:
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:
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.
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
.
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.
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.
Duration: 00:05:00
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.
replace-with-summary-form-2