Getting Started with Python scripting

In this tutorial you will learn the basics of python programming. Starting with the basic datatypes, if-else statements and loops and the usage of python functions.

< Back to Main Page

< previous tutorialnext tutorial >

Introduction

In the following we will have a look into the basics of python coding. Thereby we will focus mainly on topics that are relevant for python scripting with LIS Pro 3D. For general and fully-featured python tutorials, find a big number of sources on the web!

Warning

Take care that you have installed everything as described in the installation tutorial.

Step 1: Create a new folder for your scripts

Create a new folder for your scripts called PYTHON_TUTORIAL and add a subfolder data, where you can put data in.

Step 2: Open Miniforge Prompt

Search for the Miniforge Prompt and open it.

Note

Miniforge is only a suggestion for these tutorials. You can use any other python package manager such as Anaconda, Miniconda, etc. or any python installation running without a package manager.

This will look like this:

Step 3: Go to your project directory

Copy the path of your project folder (CTRL - C)

In Miniforge Prompt type cd + space and right-click after the space in order to paste the path you have copied. Type Enter!

Thereby you change your current directory to the project directory

Step 4: Start your own conda environment

Initially you are in the (base) environment of conda. Type conda activate environmentName in order to use your own environment with all the installed modules (see installation tutorial)! Type Enter!

In this case the environment is called (myenv). On the left side you can see that we are in the (myenv) environment now.

Note

Note, that after this switch, the current working directory is still the same. But you now have access to the python packages that you have installed in your environment (myenv)!!

Note

Note, that you can have different environments running, with completely different installed modules and different versions!

Step 5: Start Visual Studio Code

Type code + space + . in order to start Visual Studio Code from the project directory (the current directory). Type Enter!

Visual Studio Code will open

Note

In case you are asked if you trust the authors of what you are trying to open, click ok

If Visual Studio Code is open, it should look like this

Step 6: Create new script in your working directory

Find the EXPLORER in the upper-left section of VSCode. You can see that you are in your project folder and that you have one subfolder called data. You have no scripts yet.

Click onto the New File Button in order to add a new script. Call the script MyScript.py.

After that, the script MyScript.py is listed in your working directory.

If the script is selected, you can see it as one tab of the code editor and inspect its content. In the moment, we have one empty script!

Step 7: Check if the correct python installation is used

Before we start coding, please check if the correct python installation (the one of your environment (myenv)) is also used by VSCode

Tip

Check this at the beginning of every coding session. Especially, if you have multiple environments, with different modules installed.

Type ctrl + shift + P to go to the following drop-down menu:

Click on Python: Select Interpreter!

Here, the python installation of your environment (myenv) should be shown and should be selected. If not, add the correct path (see installation tutorial).

If everything is set correctly here, we can start scripting.

Step 8: Hello World

Type the following command into the first line of your script:

print("Hello World")

and press the play/run button in the upper-right corner of the screen

At the bottom of the screen, you can see the TERMINAL. After Execution, it is indicated that the script c:/LiDAR_Data/PYTHON_TUTORIAL/MyScript.py was executed.

And it prints: Hello World. Because we have told python to print “Hello World”.

Tip

Try out some other text to be printed and run the script again!

Step 9: What is a string?

“Hello World” is a piece of text. In python, it is an object of type string. In short, a string is a sequence of characters enclosed in quotation marks.

Step 10: What is print()?

print() is a built-in function used to display data, such as the string “Hello World” to the console or standard output stream. Thereby it serves as a means for the computer (Python program) to convey information or messages to the user by displaying text or data on the screen, allowing for interaction and information exchange between the user and the program.

Step 11: Variables

In python we can store an object like a string (e.g. “Hello World”) in a variable. A variable in programming is a symbolic name or identifier that represents a value or data storage location in a computer program (in our case it has to represent a string).

Let’s define a new variable with the name MyText. If we print this variable, we will see that it points onto a string with 11 characters: “Hello World”.

MyText = "Hello World"
print(MyText)
Hello World

Step 12: Checking the Data Type

Let’s check, if “Hello World” is actually a string.

Use the build-in function type(), which is returning the type of the variable MyText. We can store the returned value in a variable again. Let’s call the variable type_returned.

Warning

If you define new variables, you can give them any name you want, except for keywords that are already in use by python. If you would call the returned value simply type you would override the built-in function type(). If you have accidentaly chosen a name that is already in use, it will be indicated in a different color in VSCode

MyText = "Hello World"
type_returned = type(MyText)

print(type_returned)
print(MyText)
<class 'str'>
Hello World

After printing type_returned, we have verified that the variable type is a ‘str’!

Step 13: Properties of strings

Access length of string

As a string is a sequence of characters, we can access the number of characters by using the built-in function len().

MyText = "Hello World"
length = len(MyText)
print(length)
11

You can see that “Hello World” has 11 characters in it. One of the characters is a space!

Access individual characters

As a string is a sequence of characters, we can access the individual characters by an index in rectangular brackets: MyText[0]

Note

Note that the indices of the individual characters start with 0!

MyText = "Hello World"

print(MyText[0])
print(MyText[1])
print(MyText[2])
print(MyText[3])
print(MyText[4])
print(MyText[5])
print(MyText[6])
print(MyText[7])
print(MyText[8])
print(MyText[9])
print(MyText[10])
H
e
l
l
o
 
W
o
r
l
d

In this code the script prints the individual characters one after another.

Note

Note that character 5 is no letter here, but a space (which is also a character)!

Access a slice of characters

MyText = "Hello World"


print(MyText[:5]) # access all characters before the character with index **5**
print(MyText[5:]) # access all characters after the character with index **5** (including character 5)
Hello
 World

Calculating with strings

Check what happens if you add or multiply strings

Sum = MyText + MyText + MyText
Product = MyText * 5
MyText = "Hello World"

Sum = MyText + MyText + MyText
Product = MyText * 5

print(Sum) 
print(Product)
Hello WorldHello WorldHello World
Hello WorldHello WorldHello WorldHello WorldHello World

You can see that you can use addition and multiplication for a string in order to concatenate it!

A more useful concatenation of strings

Space = " "
Text0 = "Hello"
Text1 = "my"
Text2 = "name"
Text3 = "is"
Text4 = "Python!"

Sentence = Text0 + Space + Text1 + Space + Text2 + Space + Text3 + Space + Text4


print(Sentence)
Hello my name is Python!

Add a \n character

Add an “\n” character and check what it does!

n = "\n"
Space = " "
Text0 = "Hello"
Text1 = "my"
Text2 = "name"
Text3 = "is"
Text4 = "Python!"


Sentence = Text0 + n + Text1 + Space + Text2 + Space + Text3 + Space + Text4


print(Sentence)
Hello
my name is Python!

The “\n” character represents a new line statement.

Step 14: Handling Filepaths with strings

If we can concatenate any combination of characters as a string, we can also create filepaths.

This is an example filepath:

We could just write:

path = "C:\Users\Public\Downloads"
print(path)

But as you already know, the \ character can easily be interpreted as \n if by chance an n is occuring next to it. Additionally, the separator is interpreted differently when using different operating systems.

Thus we let a python module called os (operating system) help us with it.

Step 14.1 Import os module

We have already seen that python has some built-in functions that help us doing things (type(), len(), print()). For more specific tasks we need additional modules. One of these modules is the module called os for solving problems related to the operating system and filepaths. Let’s import this module in our python script.

Imports are always made at the very top of the script!

import os

Step 14.2 Define a path

import os
path = "C:" + os.sep + "Users" + os.sep + "Public" + os.sep + "Downloads"
print(path)
C:\Users\Public\Downloads
Note

This is the easiest way to define a path

Step 14.3 Get the current working directory

Using os, we can also query the current working directory and get it as a string

import os
cwd = os.getcwd()
print(cwd)

But we can also write it manually:

import os
cwd = path = "C:" + os.sep + "LiDAR_Data" + os.sep + "PYTHON_TUTORIAL"
print(cwd)
C:\LiDAR_Data\PYTHON_TUTORIAL

Step 14.4 Checking if a path exists

Using os we can check if the given path really exists. Therefore we can use the function os.path.exists(), which receives the string to be checked and returns a so called bool. A bool can only represent two value: either True or False. Thus we have learned a new data type, called bool. It can have only two values: True or False!

import os
cwd = path = "C:" + os.sep + "LiDAR_Data" + os.sep + "PYTHON_TUTORIAL"
PathExists = os.path.exists(cwd)
print(PathExists)
print(type(PathExists))
True
<class 'bool'>

In this case the cwd must exist (the variable PathExists has the value True), because it is the current working directory. However, it becomes more interesting, if we check for the existance of subfolders.

You can see, that we have a subfolder called data in our cwd:

You can also see it in the EXPLORER of VSCode.

Lets define the subfolder as a string and check, if it exists.

import os
cwd = os.getcwd()

path = cwd + os.sep + "data"

PathExists = os.path.exists(path)
print(PathExists)
True

Lets define a subfolder that is not existing:

import os
cwd = os.getcwd()

path = cwd + os.sep + "processing"

PathExists = os.path.exists(path)
print(PathExists)
False

Step 14.5 Creating new folders

Using os we can create new folders, if the queried paths are not existing. Therefore we can use the function os.makedir(), which receives the path to be created and creates the desired path in our filesystem.

Tip

We can use a so-called if-statement in order to check, if the path is not already existing, or more specific, if the variable PathExists == False. An if-statement always consists of an if, a statement and ends with a colon. Everything that has to be performed by the program, if the statement is true, has to be indented one level.

Warning

In an if-statement always use == instead of =. = sets a value for a variable, == compares two values!

import os
cwd = os.getcwd()

path = cwd + os.sep + "processing"
PathExists = os.path.exists(path)

if(PathExists == False):
    os.makedirs(path)

You can see that the path is now existing:

Step 14.6 Create some folders and subfolders

Let’s create some new folders and subfolders!

import os
cwd = os.getcwd()

path = cwd + os.sep + "processing"

raster = path + os.sep + "01_raster"
shapes = path + os.sep + "02_shapes"
pointclouds = path + os.sep + "03_point_clouds"

export = cwd + os.sep + "export"

if not os.path.exists(path):
    os.makedirs(path)

if not os.path.exists(raster):
    os.makedirs(raster)
if not os.path.exists(shapes):
    os.makedirs(shapes)
if not os.path.exists(pointclouds):
    os.makedirs(pointclouds)

if not os.path.exists(export):
    os.makedirs(export)
Note

In this example we use if not instead of if, which is simply the opposite statement.

Again, we can see that the defined paths have been generated in our cwd:

Note

Note that manipulating filepaths as described above is a helpful method in order to manage multiple files with varying filenames in an automation pipeline!

Step 15: User Input

Until now we have hardcoded the string within our script. Now we want to have a dynamic input by the user. Write the following code:

Sentence = input("Hello\nPlease provide a short text! ")
print(Sentence)

If you run this script, in the TERMINAL you will see the following Prompt:

  • Click with your left-mouse onto the white cursor at the end of the statement

  • Write a text

  • Enter

You can see that the script prints the text that we have provided.

input() is a built-in function used to collect user input from the keyboard. When input() is called, it displays a prompt to the user and waits for them to enter text. Once the user enters his/her input and presses the Enter key, the entered text is returned as a string by the input() function. The string is stored in the variable sentence and passed to the built-in function print() which is displaying the string in the console.

Note

Now we are able to recieve some input data from outside of the script, do something with it and generate a user output (print)

Step 16: Implement a quiz

Now let’s ask the user a question and tell him/her if the answer was correct or not. Therefore we have two cases that could occur. Either the answer is correct (then we want to print “correct”) or the answer is wrong (then we want to print “incorrect”)

This can be done, using the if and else statements.

Answer = input("Hello\nWhat is the first letter in the alphabet! ")

if Answer == "A":
  print("correct")
else:
  print("incorrect")

An if-statement starts with if, followed by a condition and ends with “:”. Everything that has to be executed, if the given condition is true, has to be indented one level

The else-statement statement is the counterpart of the if and also ends with “:”. Everything that has to be executed, if the given condition is not true, has to be indented one level

Note

In Python, indentation plays a crucial role in determining the structure and grouping of code, e.g. defining code blocks like if-else statements. Unlike many other programming languages that use braces or other symbols to denote code blocks, Python uses indentation for this purpose.

Warning

Note that “==” is a comparison, checking if two values are equal. A single “=” is always an assignment of a value to a variable. It is not allowed to put a single “=” into an if-statement, and you will get an error!

Note

You can make an assignment and a comparison in one line, if you assign the result of the comparison to a variable.

Answer = input("Hello\nWhat is the first letter in the alphabet! ")
result = Answer == "A"
print(result)
print(type(result))

If we print the value of the variable result. You can see that it is either True or False. Thus it is a bool again. It can have only two values: True or False!

We can change the code of our quiz as follows:

Answer = input("Hello\nWhat is the first letter in the alphabet! ")

result = Answer == "A"
print(result)


if (result == True):
  print("correct")
else:
  print("incorrect")

Step 16.1: Add an and-/or-statement

In the moment we have the possibility that the user types in ether a lower case or an upper case letter. Lets check if the letter is “A” and if the letter is upper case.

We can change the code of our quiz as follows and use an and-statement:

Answer = input("Hello\nWhat is the first letter in the alphabet! ")

result = Answer == "A" and Answer.isupper == True
print(result)

if (result == True):
  print("correct")
else:
  print("incorrect")
Note

Note that the result is now only true, if both conditions are true! E.g. F would be an uppercase letter, but is not identical with “A”!

But in or case this is not very helpful and does not improve our code a lot. What we want to do instead is to be more tolerant and allow the user to type in both the lower case and the upper case letter of “A”. Therefore we use an or-statement:

Answer = input("Hello\nWhat is the first letter in the alphabet! ")

result = Answer == "A" or  Answer == "a"
print(result)

if (result == True):
  print("correct")
else:
  print("incorrect")
Note

Note that the result is now true, if at least one of both options is true! “A” or “a”!

Step 17: Implement a multiple choice quiz

Let’s give the user four suggestions (A,B,C,D) in order to decide, which is the correct solution. Therefore we can use the elif (else if) statement additionally:

Answer = input("Hello\nWhat is the first letter in the alphabet? A, B, C or D")

if Answer == "A" or Answer == "a":
  print("correct")
elif Answer == "B" or Answer == "b":
  print("incorrect")
elif Answer == "C" or Answer == "c":
  print("incorrect")
elif Answer == "D" or Answer == "d":
  print("incorrect")
else:
  print("answer was not available for selection")

Step 18: Import some additional modules

We have already used the module called os. Additional modules are the module called math for math operation (square root, round, cos, sin, tan…) and the module called random in order to generate pseudo-random numbers.

Import the math module and the random module!

import math
import random

Step 19: Implement a primary school math quiz

Lets implement a math quiz for the multiplication of values between 1 and 10, e.g. 3*6 = 18.

First generate two random numbers between 1 and 10 using the random.uniform() function of the random module

Step 19.1: Create random numbers

import math
import random

Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)

Step 19.2: Print the numbers and their datatypes

import math
import random

Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)

print(Value1)
print(Value2)
print(type(Value1))
print(type(Value2))
5.153774961125492
9.208627557392852
<class 'float'>
<class 'float'>

You can see that the function random.uniform generates a decimal number between 1 and 10. Value1 and Value2 are both decimal numbers and 13 decimal places get printed.

In python these numbers are represented as floating point numbers. A floating point number is another datatype in python. If we query the datatype, we see that it is simply called float.

Step 19.3: Floating point calculation

It is no surprise, that we can make calculations with the given floating point values.

import math
import random

Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)

Sum = Value1 + Value2
Difference = Value1 - Value2
Product = Value1 * Value2
Quotient = Value1 / Value2

print("Values: ", Value1, Value2)
print("Sum: ", Sum)
print("Difference: ", Difference)
print("Product: ", Product)
print("Quotient: ", Quotient)
Values:  7.869231722805392 6.255919592749745
Sum:  14.125151315555136
Difference:  1.6133121300556468
Product:  49.22928091458608
Quotient:  1.2578856882887983
Note

Note that the built-in function print() can take multiple arguments in order to print multiple values! In the first print-statement we print one string (“Values:”), a float (Value1) and another float (Value2).

Note also that there is a difference between “Sum:”, which is a string and will be printed as text, and Sum (without quotation marks), which is the variable name for a float and the assigned decimal number will be printed!

Warning

If you want to use a variable (e.g. Sum) for e.g. a print-statement, it has to be defined before. Take care of lower and upper case.

Step 19.4: Rounding and changing the datatype

If we remember that we wanted to make a primary school math quiz, which can be done by calculating in our heads. Calculations with decimal numbers might be too difficult. Thus we want to round the given numbers.

Round the values to only 2 digits by using the build-in function round(). The function takes two arguments: the variable value to be rounded and the number of decimals we want to round to.

import math
import random

Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)

Value1 = round(Value1,2)
Value2 = round(Value2,2)

Product = Value1 * Value2

print("Values: ", Value1, Value2)
print("Product: ", Product)
Values:  8.31 8.33
Product:  69.2223
Warning

Note that we use the same variable name as an argument for the round() function but also for the returned result. Thereby, we overwrite the original value of the variable!

You can see that we now have only 2 instead of 15 decimal positions.

Let’s round to the nearest integer by setting the decimals parameter to 0

import math
import random

Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)

Value1 = round(Value1,0)
Value2 = round(Value2,0)

Product = Value1 * Value2

print("Values: ", Value1, Value2)
print("Product: ", Product)
Values:  8.0 1.0
Product:  8.0

Now, you can see that we were successful and the decimal places are zero. However, one decimal place is still shown here, which shows us that python still interpretes this value as float, although it obviously has been rounded to the nearest integer.

In order to let python know, that our values have to be interpreted as integers we have to cast the variable to an integer using int().

import math
import random

Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)

Value1 = round(Value1,0)
Value2 = round(Value2,0)

Value1 = int(Value1) #casting to integer
Value2 = int(Value2) #casting to integer

Product = Value1 * Value2

print("Values: ", Value1, Value2)
print("Product: ", Product)
Values:  7 3
Product:  21

Now we have two integers created and python prints the given values without decimals now. int is an additional datatype in python and we can calculate with it. Integers are perfect for enumerations, representing the number of objects. It can also be used as an index to access individual entries in a sequence like a string

Note

The len() function returns an int as it describes the number of entries. If we use string[i] to get an individual character from a string, i has to be an int.

Warning

Again we use the same variable name as an argument for the int() function but also for the returned result. Thereby, we overwrite the original value of the variable! In this case the datatype of the variable is changing from float to int!

Tip

You can switch into any datatype if it makes sense (str(), float(), bool())!

Tip

Try to change the values to string (str())! Can you explain, what is happending here? Why is an addition working and a multiplication not?

import math
import random

Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)

Value1 = round(Value1,0)
Value2 = round(Value2,0)

Value1 = str(Value1)
Value2 = str(Value2)

Sum = Value1 + Value2

print("Values: ", Value1, Value2)
print("Sum: ", Sum)
Values:  2.0 1.0
Sum:  2.01.0

Step 19.5: Create the quiz

import math
import random

# first let python pre-calculate a math problem
Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)
Value1 = round(Value1,0)
Value2 = round(Value2,0)
Value1 = int(Value1)
Value2 = int(Value2)

Product = Value1 * Value2

# let's formulate a question
Question = "Hello\nWhat is the product of " + str(Value1) + " and " + str(Value2) + "? "

Answer = input(Question)

# the Answer is initially a string and has to be converted into an integer before comparison 
Answer = int(Answer)

if Product == Answer:
  print("Perfect, this was correct!")
else:
  print("Sorry, this was incorrect! The result is: ", Product)

Step 19.6: Measure the elapsed time

To make the quiz more challenging, let’s measure the elapsed time for an answer.

Therefore we use the python module time

import time

# Record the start time
start_time = time.time()

# Perform some operations
# ...

# Record the end time
end_time = time.time()

# Calculate the elapsed time
elapsed_time = end_time - start_time

Let’s add this to our math quiz:

import math
import random
import time

# Record the start time
start_time = time.time()

# first let python pre-calculate a math problem
Value1 = random.uniform(1, 10)
Value2 = random.uniform(1, 10)
Value1 = round(Value1,0)
Value2 = round(Value2,0)
Value1 = int(Value1)
Value2 = int(Value2)

Product = Value1 * Value2

# let's formulate a question
Question = "Hello\nWhat is the product of " + str(Value1) + " and " + str(Value2) + "? "

Answer = input(Question)

# the Answer is initially a string and has to be converted into an integer before comparison 
Answer = int(Answer)

# Record the elapsed time
current_time = time.time()
elapsed_time = int(current_time - start_time)

if Product == Answer:
  print("Perfect, this was correct!")
  print("You made it in ", elapsed_time, " seconds")
else:
  print("Sorry, this was incorrect! The result is: ", Product)
  print("Maybe too quick, (you made it in ", elapsed_time, " seconds)")

Step 19.7: While-Loop - Asking multiple questions in 30 seconds

As you have noticed, in the moment we are only able to ask a single question, evaluate it and show the results. Thereby python interpretes each line of written code one after the other, if the code has finished also python finishes the program.

But how can we ask multiple questions? Let’s tell python to repeat a part of the code until a certain criterion is given. This can be done by the while(): statement. It takes a bool-argument, which usually is the result of a comparison. while(this is true): do this, else, stop. The while():-statement ends with a “:” and everything that has to be done, when the argument is true, has to be indented as code block.

In our case we can use the elapsed time to control the while statement


start_time = time.time()
stop_time = 30 # only run 30 seconds
elapsed_time = 0

while(elapsed_time < stop_time):

  # Do something

  current_time = time.time()
  elapsed_time = int(current_time - start_time)

Adapt this for our quiz:

import math
import random
import time

# Record the start time
start_time = time.time()

#Set stopwatch
stop_time = 30 # only run 30 seconds
elapsed_time = 0

while(elapsed_time < stop_time):

    # first let python pre-calculate a math problem
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "

    Answer = input(Question)

    # the Answer is initially a string and has to be converted into an integer before comparison 
    Answer = int(Answer)
    if Product == Answer:
        print("Perfect, this was correct!")
    else:
        print("Sorry, this was incorrect! The result is: ", Product)

        # Record the elapsed time
    current_time = time.time()
    elapsed_time = int(current_time - start_time)

Step 19.8: Add counters

The program will repeat asking for 30 seconds and the user can try to be fast in answering the questions. This is nice but it would be great, if we could just ask the questions and the user gets an overall result for this quiz. It would be interesting to have the total number of asked questions, the number of correct answers and so on.

Therefore, we have to remember that everything, which is indented within the while loop is repeated until the given stop criterion evaluates to False.

Thus, before the loop, we can define an integer called i and assign the value 0. In the while loop we can then increase the value of i by 1 (Note, this is a counter). Additionally, we could have a second counter that only is incremented when the given answer was correct.

import math
import random
import time

# Record the start time
start_time = time.time()

#Set stopwatch
stop_time = 30 # only run 30 seconds
elapsed_time = 0

#Define counters
i = 0
i_correct = 0
i_incorrect = 0

while(elapsed_time < stop_time):

    # first let python pre-calculate a math problem
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "

    Answer = input(Question)

    # the Answer is initially a string and has to be converted into an integer before comparison 
    Answer = int(Answer)
    if Product == Answer:
        # increase the value of i_correct by 1
        i_correct+=1
    else:
        # increase the value of i_incorrect by 1
        i_incorrect+=1

        # Record the elapsed time
    current_time = time.time()
    elapsed_time = int(current_time - start_time)

    # increase the value of i by 1
    i+=1

# print the results of the quiz after the time is over
print("")
print("#### Your Results ####")
print("Correct Answers: ", i_correct)
print("Incorrect Answers: ", i_incorrect)
print("Total Answers: ", i)
print("Score: ", (i_correct/i)*100)

Step 19.9: Abort option for user

It might be convenient for the user to abort the quiz before it is actually over. Let’s use “Q” for quitting the quiz and “S” for skipping the question.

Therefore we can use the break and the continue statement. Break will stop the loop and python will interprete the next code line, which is not indented within the while-loop. Continue will only jump to the top of the while loop code again and skip the rest of the code within the while-code-block.

if Answer == "Q" or Answer == "q":
    break
if Answer == "S" or Answer == "s":
    continue
Warning

This has to be evaluated, before the answer has been converted to integer, because both “Q” and “S” can’t be converted to integer!

import math
import random
import time

# Record the start time
start_time = time.time()

#Set stopwatch
stop_time = 30 # only run 30 seconds
elapsed_time = 0

#Define counters
i = 0
i_correct = 0
i_incorrect = 0

print(" ")
print("#### Welcome to the Quiz ####")
print("Type S to skip the question")
print("Type Q to quit the quiz")
print(" ")

while(elapsed_time < stop_time):

    # first let python pre-calculate a math problem
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "

    Answer = input(Question)

    ## allow the user to quit or skip
    if Answer == "Q" or Answer == "q":
        i+=1
        break
    if Answer == "S" or Answer == "s":
        i+=1
        continue

    # the Answer is initially a string and has to be converted into an integer before comparison 
    Answer = int(Answer)
    if Product == Answer:
        # increase the value of i_correct by 1
        i_correct+=1
    else:
        # increase the value of i_incorrect by 1
        i_incorrect+=1

        # Record the elapsed time
    current_time = time.time()
    elapsed_time = int(current_time - start_time)

    # increase the value of i by 1
    i+=1

# print the results of the quiz after the time is over
print("")
print("#### Your Results ####")
print("Correct Answers: ", i_correct)
print("Incorrect Answers: ", i_incorrect)
print("Total Answers: ", i)
print("Score: ", (i_correct/i)*100)

Step 19.10: Using a FOR-Loop

Our quiz runs for a defined period of time and counts the number of answered questions. But we could also do it the other way around and define a number of questions and measure the elapsed time the user has needed to answer these questions.

This can be done with a for-loop instead of a while-loop. A For-loop includes an iterator which is incremented by (e.g.) 1 in each iteration of the loop.

for i in range(1,11):

This loop performs exactly 10 iterations. It starts with index 1 and ends with index 10 (1 before 11).

Warning

Note that the variable i is defined within this loop and replaces our own definition of i as a counter! Thus we should not increment the counter i by ourselves as we did in the code above (i+=1)!

import math
import random
import time

# Record the start time
start_time = time.time()

#set number of questions
num_questions = 11 

#Set stopwatch
elapsed_time = 0

#Define counters
i_correct = 0
i_incorrect = 0

print(" ")
print("#### Welcome to the Quiz ####")
print("Type S to skip the question")
print("Type Q to quit the quiz")
print(" ")

for i in range(1,num_questions):

    # first let python pre-calculate a math problem
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "

    Answer = input(Question)

    if Answer == "Q" or Answer == "q":
        break
    if Answer == "S" or Answer == "s":
        continue

    # the Answer is initially a string and has to be converted into an integer before comparison 
    Answer = int(Answer)
    if Product == Answer:
        # increase the value of i_correct by 1
        i_correct+=1
    else:
        # increase the value of i_incorrect by 1
        i_incorrect+=1

        # Record the elapsed time
    current_time = time.time()
    elapsed_time = int(current_time - start_time)

total_time = int(current_time - start_time)
# print the results of the quiz after the time is over
print("")
print("#### Your Results ####")
print("Elapsed Time: ", total_time)
print("Correct Answers: ", i_correct)
print("Incorrect Answers: ", i_incorrect)
print("Total Answers: ", i)

print("Score: ", (i_correct/i)*100)
Note

Note that we also measure the total_time now after the loop has finished (not indented within the for-loop)!

Step 19.11: Using lists in python

In the moment we only have a look onto the overall score, but not on all the questions in detail. An additional feature for the quiz would be a detailed overview with all the asked questions and all the answers besides the overall score.

In order to have an overall print of all the questions and answers at the end of the quiz, we need to have a possibility to remember all of this information and recall this memory in the end.

Keep in mind that python interpretes the code line by line. If one line has been executed, it is gone. Thus, we need an object, where we can store this information as a sequence, and recall its information after the for-loop has finished.

Let’s use a python list, which is a sequential datatype like a string, but it cannot only store a sequence of characters but a sequence of all available python datatypes.

The objects within the list are enclosed in square brackets.

## a list with many different datatypes
# a string
# an int
# a float
# a bool
# a list (within a list)
MyList = ["Hello", 1, 3.89, True, [1,2,3]]

A list can have a dynamic length. We can define a variable MyList and assign an empty list to it. Later, we can append elements to the list one by one using the append-method of the list. See the following code:

MyList = []
print(len(MyList))

MyList.append("Hello")
MyList.append(1)
MyList.append(3.89)
MyList.append(True)
MyList.append([1,2,3])

print(MyList)
print(len(MyList))
0
['Hello', 1, 3.89, True, [1, 2, 3]]
5
Note

Initially, we have a list with 0 entries. Note that we fill up the list step by step and in the end it has 5 entries!

Tip

We can use lists to store information and recall it at a later time!

Thus, we could use lists in order to remember the different questions and answers of our quiz. This is an idea how it could work:

QuestionList = []

for i in range(1,6):

    Question = "What is the product of 3 and 4"
    Answer = 15
    Result = False
    # ask and process question

    Summary = [Question,Answer,Result]
    QuestionList.append(Summary)
print(QuestionList)
[['What is the product of 3 and 4', 15, False], ['What is the product of 3 and 4', 15, False], ['What is the product of 3 and 4', 15, False], ['What is the product of 3 and 4', 15, False], ['What is the product of 3 and 4', 15, False]]

In this code we create a new empty list called QuestionList. After getting the question and answer (which is always incorrect here, indeed) we create a list called Summary including the question (string), the answer (int) and the result (bool). In each iteration and for each question the summary is appended to the QuestionList

We print the list after all questions have finished and you can see that QuestionList is a list containing 5 summary lists now.

The whole list is printed in a single line which is a bit messy. Let’s print the individual entries of QuestionList one after another!

Therefore we can use a for-loop again that increments the variable i by starting with 0 and ending with the result of len(QuestionList) (the total number of entries within the list). We can use the incremented index i in order to access the individual entries of QuestionList (QuestionList[i]).

QuestionList = []

for i in range(1,6):

    Question = "What is the product of 3 and 4"
    Answer = 15
    Result = False
    # ask and process question

    Summary = [Question,Answer,Result]
    QuestionList.append(Summary)

for i in range(0, len(QuestionList)):
    print(QuestionList[i])
['What is the product of 3 and 4', 15, False]
['What is the product of 3 and 4', 15, False]
['What is the product of 3 and 4', 15, False]
['What is the product of 3 and 4', 15, False]
['What is the product of 3 and 4', 15, False]

You can see that now the entries are printed one after another.

Note

Note that there is also a shorter version for iterating through the QuestionList!

for entry in QuestionList:
    print(entry)

This type of for-loop provides the same result, but avoids to use an iterator and directly returns the entry of the list that we want to have. Thus we don’t have to use the iterator anymore in order to access the individual entries (QuestionList[i])

Let’s print also the different entries of the summary list separately:

QuestionList = []

for i in range(1,6):

    Question = "What is the product of 3 and 4"
    Answer = 15
    Result = False
    # ask and process question

    Summary = [Question,Answer,Result]
    QuestionList.append(Summary)

for entry in QuestionList:
    print(entry[0],entry[1],entry[2])
What is the product of 3 and 4 15 False
What is the product of 3 and 4 15 False
What is the product of 3 and 4 15 False
What is the product of 3 and 4 15 False
What is the product of 3 and 4 15 False

Let’s wrap the whole result into a nice text again using string-concatenation:

QuestionList = []

for i in range(1,6):

    Question = "What is the product of 3 and 4"
    Answer = 15
    Result = False
    # ask and process question

    Summary = [Question,Answer,Result]
    QuestionList.append(Summary)

for entry in QuestionList:
    SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
    print(SummaryString)
Question: What is the product of 3 and 4, Answer: 15, Result: False
Question: What is the product of 3 and 4, Answer: 15, Result: False
Question: What is the product of 3 and 4, Answer: 15, Result: False
Question: What is the product of 3 and 4, Answer: 15, Result: False
Question: What is the product of 3 and 4, Answer: 15, Result: False

Let’s integrate this idea into our script:

import math
import random
import time

# Record the start time
start_time = time.time()

#set number of questions
num_questions = 11 # only run 30 seconds

#Set stopwatch
elapsed_time = 0

#Define counters
i_correct = 0
i_incorrect = 0

print(" ")
print("#### Welcome to the Quiz ####")
print("Type S to skip the question")
print("Type Q to quit the quiz")
print(" ")

QuestionList = []
for i in range(1,num_questions):

    # first let python pre-calculate a math problem
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "

    Answer = input(Question)

    if Answer == "Q" or Answer == "q":
        break
    if Answer == "S" or Answer == "s":
        continue

    # the Answer is initially a string and has to be converted into an integer before comparison 
    Answer = int(Answer)

    Result = False
    if Product == Answer:
        Result = True
        i_correct+=1
    else:
        i_incorrect+=1

        # Record the elapsed time
    current_time = time.time()
    elapsed_time = int(current_time - start_time)

    Summary = [Question,Answer,Result]
    QuestionList.append(Summary)

total_time = int(current_time - start_time)
# print the results of the quiz after the time is over
print("")
print("#### Your Results ####")
print("Elapsed Time: ", total_time)
print("Correct Answers: ", i_correct)
print("Incorrect Answers: ", i_incorrect)
print("Total Answers: ", i)
print("Score: ", (i_correct/i)*100)
print("")

for entry in QuestionList:
    SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
    print(SummaryString)

Step 19.20: Write the result as a textfile and save it

Maybe the user wants to save the results, in order to have the results available even after shutting down the computer. Therefore we can write a textfile with the results.

We define a variable fobj pointing on a file stream. The function open() opens a textfile (path to the file provided as a string). If the given flag is “w”, it writes a new textfile and saves it at the given location. With the open filestream, we can use the write()-method of the filestream in order to write into the file.

#define a variable **fobj** pointing on a file stream
fobj = open("QuizResults.txt","w")

ResultString = "This is my result!\n"

# write the string to the textfile
fobj.write(ResultString)
fobj.close()
Note

Note that \n is used, in order to control the formatting in multiple lines!

Let’s add this feature to our quiz:

import math
import random
import time

# Record the start time
start_time = time.time()

#set number of questions
num_questions = 11 # only run 30 seconds

#Set stopwatch
elapsed_time = 0

#Define counters
i_correct = 0
i_incorrect = 0

print(" ")
print("#### Welcome to the Quiz ####")
print("Type S to skip the question")
print("Type Q to quit the quiz")
print(" ")

QuestionList = []
for i in range(1,num_questions):

    # first let python pre-calculate a math problem
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "

    Answer = input(Question)

    if Answer == "Q" or Answer == "q":
        break
    if Answer == "S" or Answer == "s":
        continue

    # the Answer is initially a string and has to be converted into an integer before comparison 
    Answer = int(Answer)

    Result = False
    if Product == Answer:
        Result = True
        i_correct+=1
    else:
        i_incorrect+=1

        # Record the elapsed time
    current_time = time.time()
    elapsed_time = int(current_time - start_time)

    Summary = [Question,Answer,Result]
    QuestionList.append(Summary)

total_time = int(current_time - start_time)
# print the results of the quiz after the time is over
print("")
print("#### Your Results ####")
print("Elapsed Time: ", total_time)
print("Correct Answers: ", i_correct)
print("Incorrect Answers: ", i_incorrect)
print("Total Answers: ", i)
print("Score: ", (i_correct/i)*100)
print("")

fobj = open("QuizResults.txt","w")
fobj.write("#### Your Results ####\n")
fobj.write("Elapsed Time: " + str(total_time) + "\n")
fobj.write("Correct Answers: " + str(i_correct) + "\n")
fobj.write("Incorrect Answers: " + str(i_incorrect) + "\n")
fobj.write("Total Answers: " + str(i) + "\n")
fobj.write("Score: " + str((i_correct/i)*100) + "\n\n")


for entry in QuestionList:
    SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
    print(SummaryString)
    fobj.write(SummaryString + "\n")
fobj.close()

After execution a new textfile appears in the EXPLORER of VSCode. View the textfile in the Editor!

Step 19.21: Reading a textfile

Now as we allow the user to store his results, we could evaluate how he/she improves over multiple days or weeks. Therefore we could read the information from previous runs from the file, in case the file exists.

First we import the python module os (operating system). The os-function os.path.exists() allows to check, if the provided path exists. In this case it is the relative path from the current working directory (“QuizResults.txt” is in the same folder as MyScript.py).

If the file exists, we open a filestream again. We define a variable fobj_in pointing on a file stream. The function open() opens a textfile (path to the file provided as a string). If the given flag is “r”, it reads an existing textfile from the given location.

import os

file_path = "QuizResults.txt"

if os.path.exists(file_path):
    fobj_in = open(file_path, "r")

If we have the filestream open, we can iterate through the different rows of the document, using a for-loop again. Let’s print the second row of the file (this is the row with index 1)

import os

file_path = "QuizResults.txt"

if os.path.exists(file_path):
    fobj_in = open(file_path, "r")
    for index, element in enumerate(fobj_in):
        if index == 1:
            print(element)
            print(type(element))
    fobj_in.close()
Elapsed Time: 43

<class 'str'>
Note

This is another version of a for-loop, providing both an index and the entry together. Before we have seen for-loops providing either the index (for i in range(len(List)):) or the entry (for entry in List:)

Step 19.22: Parse a value from the textfile

Now we want to extract the Elapsed Time information as an int in order to make it comparable to other Elapsed Time informations.

The variable element that we read here is a string. The string includes both the description and the time value itself. In order to extract the number from the string, we first split the string using the ” “ character as a splitter:

element = "Elapsed Time: 43\n"
element = element.strip() # eliminiates the newline statement
element = element.split(" ") 
print(element)
print(type(element))
['Elapsed', 'Time:', '43']
<class 'list'>
Note

Doing so, the string is split, resulting in a list containing multiple strings.

Now, we can grab the third element of the list (“43”) and convert it to int using int().

element = "Elapsed Time: 43\n"
element = element.strip() # eliminiates the newline statement
element = element.split(" ") 
top_score_time = int(element[2])
print(top_score_time)
43

This allows us to read the top score time from the textfile and store it in the variable top_score_time.

import os

file_path = "QuizResults.txt"

if os.path.exists(file_path):
    fobj_in = open(file_path, "r")
    for index, element in enumerate(fobj_in):
        if index == 1:
            element = element.strip() # eliminiates the newline statement
            element = element.split(" ") 
            top_score_time = int(element[2])
            print("Your top score time is: ", top_score_time)
    fobj_in.close()

In the code below, we can compare the top score time with the elapsed time from the current questionaire. If it is shorter, we can update the top score.

if total_time < top_score_time:
    top_score_time = total_time
    print("Congratulations, you have a new top score time!") 

The total python script for this quiz will look like this:

import math
import random
import time
import os

# Record the start time
start_time = time.time()

#set number of questions
num_questions = 11 # only run 30 seconds

#Set stopwatch
elapsed_time = 0
current_time = start_time

#Define counters
i_correct = 0
i_incorrect = 0

print(" ")
print("#### Welcome to the Quiz ####")
print("Type S to skip the question")
print("Type Q to quit the quiz")
print(" ")



file_path = "QuizResults.txt"
top_score_time = 100012
if os.path.exists(file_path):
    fobj_in = open(file_path, "r")
    for index, element in enumerate(fobj_in):
        if index == 1:
            element = element.strip() # eliminiates the newline statement
            element = element.split(" ") 
            top_score_time = int(element[2])
            print("Your top score time is: ", top_score_time)
            print(" ")
    fobj_in.close()

QuestionList = []
for i in range(1,num_questions):

    # first let python pre-calculate a math problem
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "

    Answer = input(Question)

    if Answer == "Q" or Answer == "q":
        break
    if Answer == "S" or Answer == "s":
        continue

    # the Answer is initially a string and has to be converted into an integer before comparison 
    Answer = int(Answer)

    Result = False
    if Product == Answer:
        Result = True
        i_correct+=1
    else:
        i_incorrect+=1

        # Record the elapsed time
    current_time = time.time()
    elapsed_time = int(current_time - start_time)

    Summary = [Question,Answer,Result]
    QuestionList.append(Summary)

total_time = int(current_time - start_time)

if total_time < top_score_time:
    top_score_time = total_time
    print("") 
    print("Congratulations, you have a new top score time!") 
# print the results of the quiz after the time is over
print("")
print("#### Your Results ####")
print("Elapsed Time: ", total_time, " Top Score: ", top_score_time )
print("Correct Answers: ", i_correct)
print("Incorrect Answers: ", i_incorrect)
print("Total Answers: ", i)
print("Score: ", (i_correct/i)*100)
print("")

fobj = open(file_path,"w")
fobj.write("#### Your Results ####\n")
fobj.write("TopScore Time: " + str(top_score_time) + "\n")
fobj.write("Correct Answers: " + str(i_correct) + "\n")
fobj.write("Incorrect Answers: " + str(i_incorrect) + "\n")
fobj.write("Total Answers: " + str(i) + "\n")
fobj.write("Score: " + str((i_correct/i)*100) + "\n\n")

for entry in QuestionList:
    SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
    print(SummaryString)
    fobj.write(SummaryString + "\n")
fobj.close()

Step 20: clean your code with functions

You can see that we have already accumulated a bunch of code, which is a little bit messy, while reading becomes more and more challenging. In this case, we can help us in order to encapsulate small subtasks (e.g. reading, calculating, writing, etc… ) in a python function.

We can define a function, using the def(): statement. A function has a name that we can specify as a programmer and can receive zero, one or multiple input arguments from the user. The def() statement ends with a “:”. The work do be done within the function is then defined in an indented code block.

A function can return an output result value.

def MyFunction(Argument(optional)):
    #DoSomething with Argument
    return(result(optional))

You should put your function definitions to the top of your code. Let’s see, where it makes sense to encapsulate some code into a function.

If we look into our math-quiz code, the first thing that we do is printing a welcome message. We can encapsulate these lines of code into a function called print_welcome().

def print_welcome():
    print(" ")
    print("#### Welcome to the Quiz ####")
    print("Type S to skip the question")
    print("Type Q to quit the quiz")
    print(" ")
Note

We do not need neither an input argument nor a return, because we only want to print a standard welcome message!

Next we read out the top_score. We can encapsulate this task into a function called read_top_score()

def read_top_score(file_path):
    top_score_time = 1000
    if os.path.exists(file_path):
        fobj_in = open(file_path, "r")
        for index, element in enumerate(fobj_in):
            if index == 1:
                element = element.strip() # eliminiates the newline statement
                element = element.split(" ") 
                top_score_time = int(element[2])
                print("Your top score time is: ", top_score_time)
                print(" ")
        fobj_in.close()
    return(top_score_time)
Note

In order to let the function know the filepath, where the top_score is stored, we need to pass the file_path as an input argument! And the function should provide the top_score_time. Thus we need to return the top_score_time.

Then we have the code block pre-calculating the path problem for the quiz. As a function it looks like this:

def getMathProblem():
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "
    return(Product,Question)
Note

We do not need an input argument here, because the values are generated by the random function. However, we need to return two objects: The Product and the formulated Question.

The next task is the evaluation of the given answer:

def evaluate_answer(Answer, Product, i_correct, i_incorrect):
    Answer = int(Answer)

    Result = False
    if Product == Answer:
        Result = True
        i_correct+=1
    else:
        i_incorrect+=1
    return(Result, i_correct, i_incorrect)
Note

This function needs a bunch of input arguments, in order to do the evaluation correctly. And it also needs to return a bunch of outputs that can be processed in the next steps

Additional tasks are the printing of results and writing the score to file:

def print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    if total_time < top_score_time:
        top_score_time = total_time
        print("") 
        print("Congratulations, you have a new top score time!") 

    print("")
    print("#### Your Results ####")
    print("Elapsed Time: ", total_time, " Top Score: ", top_score_time )
    print("Correct Answers: ", i_correct)
    print("Incorrect Answers: ", i_incorrect)
    print("Total Answers: ", i)
    print("Score: ", (i_correct/i)*100)
    print("")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        print(SummaryString)

def write_results(file_path, total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    fobj = open(file_path,"w")
    fobj.write("#### Your Results ####\n")
    fobj.write("TopScore Time: " + str(top_score_time) + "\n")
    fobj.write("Correct Answers: " + str(i_correct) + "\n")
    fobj.write("Incorrect Answers: " + str(i_incorrect) + "\n")
    fobj.write("Total Answers: " + str(i) + "\n")
    fobj.write("Score: " + str((i_correct/i)*100) + "\n\n")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        fobj.write(SummaryString + "\n")
    fobj.close()

20.1 Put your functions into a new script!

Make a new script called MyScript2.py If you have defined all the functions, the top of your code should look like this:

import math
import random
import time
import os


#####################################################
########### function definitions ####################
#####################################################
def print_welcome():
    print(" ")
    print("#### Welcome to the Quiz ####")
    print("Type S to skip the question")
    print("Type Q to quit the quiz")
    print(" ")

def read_top_score(file_path):
    top_score_time = 1000
    if os.path.exists(file_path):
        fobj_in = open(file_path, "r")
        for index, element in enumerate(fobj_in):
            if index == 1:
                element = element.strip() # eliminiates the newline statement
                element = element.split(" ") 
                top_score_time = int(element[2])
                print("Your top score time is: ", top_score_time)
                print(" ")
        fobj_in.close()
    return(top_score_time)

def getMathProblem():
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "
    return(Product,Question)

def evaluate_answer(Answer, Product, i_correct, i_incorrect):
    Answer = int(Answer)

    Result = False
    if Product == Answer:
        Result = True
        i_correct+=1
    else:
        i_incorrect+=1
    return(Result, i_correct, i_incorrect)

def print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    if total_time < top_score_time:
        top_score_time = total_time
        print("") 
        print("Congratulations, you have a new top score time!") 

    print("")
    print("#### Your Results ####")
    print("Elapsed Time: ", total_time, " Top Score: ", top_score_time )
    print("Correct Answers: ", i_correct)
    print("Incorrect Answers: ", i_incorrect)
    print("Total Answers: ", i)
    print("Score: ", (i_correct/i)*100)
    print("")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        print(SummaryString)

def write_results(file_path, total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    fobj = open(file_path,"w")
    fobj.write("#### Your Results ####\n")
    fobj.write("TopScore Time: " + str(top_score_time) + "\n")
    fobj.write("Correct Answers: " + str(i_correct) + "\n")
    fobj.write("Incorrect Answers: " + str(i_incorrect) + "\n")
    fobj.write("Total Answers: " + str(i) + "\n")
    fobj.write("Score: " + str((i_correct/i)*100) + "\n\n")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        fobj.write(SummaryString + "\n")
    fobj.close()

#####################################################
########### end function definitions ################
#####################################################
Note

If you run this script, it will do nothing! This is because a code within a function is only executed if the function is called! Functions are called as follows (compare to function definitions above):

print_welcome() #no argument, no return
top_score_time = read_top_score(file_path) # one argument, one return

Usually we can structure our code into a section where functions are defined (above) and a section where the actual execution takes place. For the latter we recommend to define a main-function using a if name == ‘main’: statement. Thereby, the scipt will look like this:

import math
import random
import time
import os


#####################################################
########### function definitions ####################
#####################################################
def print_welcome():
    print(" ")
    print("#### Welcome to the Quiz ####")
    print("Type S to skip the question")
    print("Type Q to quit the quiz")
    print(" ")

def read_top_score(file_path):
    top_score_time = 1000
    if os.path.exists(file_path):
        fobj_in = open(file_path, "r")
        for index, element in enumerate(fobj_in):
            if index == 1:
                element = element.strip() # eliminiates the newline statement
                element = element.split(" ") 
                top_score_time = int(element[2])
                print("Your top score time is: ", top_score_time)
                print(" ")
        fobj_in.close()
    return(top_score_time)

def getMathProblem():
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "
    return(Product,Question)

def evaluate_answer(Answer, Product, i_correct, i_incorrect):
    Answer = int(Answer)

    Result = False
    if Product == Answer:
        Result = True
        i_correct+=1
    else:
        i_incorrect+=1
    return(Result, i_correct, i_incorrect)

def print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    if total_time < top_score_time:
        top_score_time = total_time
        print("") 
        print("Congratulations, you have a new top score time!") 

    print("")
    print("#### Your Results ####")
    print("Elapsed Time: ", total_time, " Top Score: ", top_score_time )
    print("Correct Answers: ", i_correct)
    print("Incorrect Answers: ", i_incorrect)
    print("Total Answers: ", i)
    print("Score: ", (i_correct/i)*100)
    print("")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        print(SummaryString)

def write_results(file_path, total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    fobj = open(file_path,"w")
    fobj.write("#### Your Results ####\n")
    fobj.write("TopScore Time: " + str(top_score_time) + "\n")
    fobj.write("Correct Answers: " + str(i_correct) + "\n")
    fobj.write("Incorrect Answers: " + str(i_incorrect) + "\n")
    fobj.write("Total Answers: " + str(i) + "\n")
    fobj.write("Score: " + str((i_correct/i)*100) + "\n\n")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        fobj.write(SummaryString + "\n")
    fobj.close()

#####################################################
########### end function definitions ################
#####################################################


#####################################################
############### the main function ###################
#####################################################
if __name__ == '__main__':
    file_path = "QuizResults.txt"

    print_welcome()
    top_score_time = read_top_score(file_path)
#####################################################
############### end of main function ################
#####################################################
 
#### Welcome to the Quiz ####
Type S to skip the question
Type Q to quit the quiz
 
Your top score time is:  43
 
Note

The main code has only four lines, but it does already the welcome print and the readout of the top score!

20.2 Implement math-quiz using functions

Now we can implement the whole math quiz using the function definitions:

import math
import random
import time
import os


#####################################################
########### function definitions ####################
#####################################################
def print_welcome():
    print(" ")
    print("#### Welcome to the Quiz ####")
    print("Type S to skip the question")
    print("Type Q to quit the quiz")
    print(" ")

def read_top_score(file_path):
    top_score_time = 1000
    if os.path.exists(file_path):
        fobj_in = open(file_path, "r")
        for index, element in enumerate(fobj_in):
            if index == 1:
                element = element.strip() # eliminiates the newline statement
                element = element.split(" ") 
                top_score_time = int(element[2])
                print("Your top score time is: ", top_score_time)
                print(" ")
        fobj_in.close()
    return(top_score_time)

def getMathProblem():
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "
    return(Product,Question)

def evaluate_answer(Answer, Product, i_correct, i_incorrect):
    Answer = int(Answer)

    Result = False
    if Product == Answer:
        Result = True
        i_correct+=1
    else:
        i_incorrect+=1
    return(Result, i_correct, i_incorrect)

def print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    if total_time < top_score_time:
        top_score_time = total_time
        print("") 
        print("Congratulations, you have a new top score time!") 

    print("")
    print("#### Your Results ####")
    print("Elapsed Time: ", total_time, " Top Score: ", top_score_time )
    print("Correct Answers: ", i_correct)
    print("Incorrect Answers: ", i_incorrect)
    print("Total Answers: ", i)
    print("Score: ", (i_correct/i)*100)
    print("")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        print(SummaryString)

def write_results(file_path, total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    fobj = open(file_path,"w")
    fobj.write("#### Your Results ####\n")
    fobj.write("TopScore Time: " + str(top_score_time) + "\n")
    fobj.write("Correct Answers: " + str(i_correct) + "\n")
    fobj.write("Incorrect Answers: " + str(i_incorrect) + "\n")
    fobj.write("Total Answers: " + str(i) + "\n")
    fobj.write("Score: " + str((i_correct/i)*100) + "\n\n")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        fobj.write(SummaryString + "\n")
    fobj.close()

#####################################################
########### end function definitions ################
#####################################################


#####################################################
############### the main function ###################
#####################################################
if __name__ == '__main__':
    
    #set number of questions
    num_questions = 11 # only run 30 seconds
    file_path = "QuizResults.txt"

    #Set stopwatch
    start_time = time.time()

    #Define counters
    i_correct = 0
    i_incorrect = 0
    
    print_welcome()
    top_score_time = read_top_score(file_path)

    QuestionList = []
    for i in range(1,num_questions):

        Product, Question = getMathProblem()
        Answer = input(Question)

        if Answer == "Q" or Answer == "q":
            break
        elif Answer == "S" or Answer == "s":
            continue

        Result, i_correct, i_incorrect = evaluate_answer(Answer, Product,i_correct, i_incorrect)
       
        Summary = [Question,Answer,Result]
        QuestionList.append(Summary)

    total_time = int(time.time() - start_time)

    print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect)
    write_results(file_path, total_time, top_score_time, QuestionList, i, i_correct, i_incorrect)

#####################################################
############### end of main function ################
#####################################################
Note

Note that the main code has a limited number of lines now and the readability has improved.

Step 21: Outsourcing of functions

In order to get our quiz-code even more readable and short, we can “outsource” our function definitions to a separate file. If we have the functions defined in a separate file, we can import this file as a module and use the functions in any script we like. This also allows to use the same functions without re-implemeting them, avoiding code duplications.

As a programmer, we do not have to think of the solution of a specific problem multiple times and only have to call the function without knowing the exact algorithm behind.

21.1 Create command script

Create a new python script called lis_cmd.py and copy all the module imports and function definitions into it:

import math
import random
import time
import os


#####################################################
########### function definitions ####################
#####################################################
def print_welcome():
    print(" ")
    print("#### Welcome to the Quiz ####")
    print("Type S to skip the question")
    print("Type Q to quit the quiz")
    print(" ")

def read_top_score(file_path):
    top_score_time = 1000
    if os.path.exists(file_path):
        fobj_in = open(file_path, "r")
        for index, element in enumerate(fobj_in):
            if index == 1:
                element = element.strip() # eliminiates the newline statement
                element = element.split(" ") 
                top_score_time = int(element[2])
                print("Your top score time is: ", top_score_time)
                print(" ")
        fobj_in.close()
    return(top_score_time)

def getMathProblem():
    Value1 = random.uniform(1, 10)
    Value2 = random.uniform(1, 10)
    Value1 = round(Value1,0)
    Value2 = round(Value2,0)
    Value1 = int(Value1)
    Value2 = int(Value2)

    Product = Value1 * Value2

    # let's formulate a question
    Question = "What is the product of " + str(Value1) + " and " + str(Value2) + "? "
    return(Product,Question)

def evaluate_answer(Answer, Product, i_correct, i_incorrect):
    Answer = int(Answer)

    Result = False
    if Product == Answer:
        Result = True
        i_correct+=1
    else:
        i_incorrect+=1
    return(Result, i_correct, i_incorrect)

def print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    if total_time < top_score_time:
        top_score_time = total_time
        print("") 
        print("Congratulations, you have a new top score time!") 

    print("")
    print("#### Your Results ####")
    print("Elapsed Time: ", total_time, " Top Score: ", top_score_time )
    print("Correct Answers: ", i_correct)
    print("Incorrect Answers: ", i_incorrect)
    print("Total Answers: ", i)
    print("Score: ", (i_correct/i)*100)
    print("")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        print(SummaryString)

def write_results(file_path, total_time, top_score_time, QuestionList,i, i_correct, i_incorrect):
    fobj = open(file_path,"w")
    fobj.write("#### Your Results ####\n")
    fobj.write("TopScore Time: " + str(top_score_time) + "\n")
    fobj.write("Correct Answers: " + str(i_correct) + "\n")
    fobj.write("Incorrect Answers: " + str(i_incorrect) + "\n")
    fobj.write("Total Answers: " + str(i) + "\n")
    fobj.write("Score: " + str((i_correct/i)*100) + "\n\n")

    for entry in QuestionList:
        SummaryString = "Question: " + entry[0] + ", Answer: " + str(entry[1]) + ", Result: " + str(entry[2])
        fobj.write(SummaryString + "\n")
    fobj.close()

#####################################################
########### end function definitions ################
#####################################################

Save the script!

Note

Note that this script does nothing when executed, because it only contains the function definitions.

19.2 Delete function definitions from main script

Delete all the function definitions from main script. Now your script should only have these lines:

import math
import random
import time
import os

if __name__ == '__main__':
    
    #set number of questions
    num_questions = 11 # only run 30 seconds
    file_path = "QuizResults.txt"

    #Set stopwatch
    start_time = time.time()

    #Define counters
    i_correct = 0
    i_incorrect = 0
    
    print_welcome()
    top_score_time = read_top_score(file_path)

    QuestionList = []
    for i in range(1,num_questions):

        Product, Question = getMathProblem()
        Answer = input(Question)

        if Answer == "Q" or Answer == "q":
            break
        elif Answer == "S" or Answer == "s":
            continue

        Result, i_correct, i_incorrect = evaluate_answer(Answer, Product,i_correct, i_incorrect)
       
        Summary = [Question,Answer,Result]
        QuestionList.append(Summary)

    total_time = int(time.time() - start_time)

    print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect)
    write_results(file_path, total_time, top_score_time, QuestionList, i, i_correct, i_incorrect)
Warning

If you try to run the main script now, you will get an error message. This is because the functions you want to call here are not defined. To be more specific, python still doesn’t now where to find the function definitions.

21.2 Import command script

In order to help python finding your function definitions in the command script. You have to import lis_cmd and use the namespace lis_cmd, in order to call the function of interest (lis_cmd.print_welcome())

import lis_cmd
lis_cmd.print_welcome()

In the final main script it will look like this:

import math
import random
import time
import os
import lis_cmd

if __name__ == '__main__':
    
    #Set stopwatch
    start_time = time.time()

    #Define counters
    i_correct = 0
    i_incorrect = 0
    
    lis_cmd.print_welcome()
    top_score_time = lis_cmd.read_top_score(file_path)

    QuestionList = []
    for i in range(1,num_questions):

        Product, Question = lis_cmd.getMathProblem()
        Answer = input(Question)

        if Answer == "Q" or Answer == "q":
            break
        elif Answer == "S" or Answer == "s":
            continue

        Result, i_correct, i_incorrect = lis_cmd.evaluate_answer(Answer, Product,i_correct, i_incorrect)
       
        Summary = [Question,Answer,Result]
        QuestionList.append(Summary)

    total_time = int(time.time() - start_time)

    lis_cmd.print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect)
    lis_cmd.write_results(file_path, total_time, top_score_time, QuestionList,i, i_correct, i_incorrect)

Step 22: Outsourcing configurations

In our math quiz we have two user defined parameters: the number_of_questions and the file_path for the quiz results and the top score. While reading the code, it is not eye-catching that these two parameters can be tweaked in order to change the behaviour of the script. Therefore it is recommended to also outsource these parameters into a configuration files

Step 22.1: Create config script

Create a new python script called project_config.py and copy the parameter definitions into it:

num_questions = 11
file_path = "QuizResults.txt"

Save the script!

Step 22.2: Delete parameter definitions in main script

Delete the two lines in the main script.

num_questions = 11
file_path = "QuizResults.txt"

Step 22.3: Import config file

Import project_config in your main script and use the namespace when using the parameters (project_config.file_name, project_config.num_questions)

import project_config
project_config.file_path

The final main script will look like this:

import math
import random
import time
import os
import lis_cmd
import project_config

if __name__ == '__main__':

    #Set stopwatch
    start_time = time.time()

    #Define counters
    i_correct = 0
    i_incorrect = 0
    
    lis_cmd.print_welcome()
    top_score_time = lis_cmd.read_top_score(project_config.file_path)

    QuestionList = []
    for i in range(1,project_config.num_questions):

        Product, Question = lis_cmd.getMathProblem()
        Answer = input(Question)

        if Answer == "Q" or Answer == "q":
            break
        elif Answer == "S" or Answer == "s":
            continue

        Result, i_correct, i_incorrect = lis_cmd.evaluate_answer(Answer, Product,i_correct, i_incorrect)
       
        Summary = [Question,Answer,Result]
        QuestionList.append(Summary)

    total_time = int(time.time() - start_time)

    lis_cmd.print_results(total_time, top_score_time, QuestionList,i, i_correct, i_incorrect)
    lis_cmd.write_results(project_config.file_path, total_time, top_score_time, QuestionList,i, i_correct, i_incorrect)
Note

Now, if you want to change the file_name or the num_questions, you only need to look into the project_config.py and make changes there. In this case you don’t need to make any changes in the main script.

Conclusions

In this tutorial you have learned a lot of python basics and can solve programming problems now. In the next tutorials, we will learn how to exploit this for automating large lidar processing projects using LIS Pro 3D and Python.

< Back to Main Page

< previous tutorialnext tutorial >