General Python Programming Advice

Here is some advice that you should keep in mind while writing your code. These tips should make it easier for you to write readable and maintainable code. The most important thing about computer code is, that you will be able to understand it even after a longer period of time. Also, you will probably read more code than write it, so make sure it makes sense to both, a different person and yourself 6 months from now.

Meaningful names

Use meaningful names for variables and functions. Have a look at the following example:

def a(h, w):
    result h*w

The above function most likely takes the height and the width of an object and calculates the rectangular area of this object and returns it. This function might even come with a comment explaining what it does. But just imagine you are at the bottom of your code and there is just this function used as such:

if a(5, 12) < 35:
    print("Too small!")

What was the use of the function a() again? Do I really need to look it up? This is even worse when you import the functions from a different file, then you cannot just scroll up to have a look.

Try to avoid such a vague name pattern and use descriptive names that make sense.

def area_rectangle(height, width):
    result height*width

if area_rectangle(5, 12) < 35:
    print("Too small!")

If you now look at the code, it does make more sense, even without any comments. Also, if you have several functions with a similar goal, you may want to use a certain naming patters such as area_rectangle() and area_circle which makes it clear by their names that they have some similarities. The same applies to the names of variables. r is not the best variable name for the radius of a circle, therefore, use radius_circle instead, or something else that is more descriptive.

You may even want to add the measurement unit inside the name such as battery_level_mV so you will immediately see that this value is given in mili-volts instead of volts.

Avoid hard-coded values

When you implement some code with numbers or strings inside that code, you could either type the values you want into the code, or you could use a variable instead. If you implement the code by typing the actual values, these values are called hard-coded as you cannot easily change them.

Why is this bad? When you have a value that changes during the development phase, you will need to find this value in the code on each instance you use this value, and replace it. This can be a very time-consuming process while hard-coded values do not have any real advantage.

In some programming languages, you can implement these values as constants which are variables whose value cannot be changed. In Python, constants do not exist so it is common practice to use a variable written entirely in capital letters.

MIN_VOLTAGE = 1.2
MAX_VOLTAGE = 1.7
PI = 3.141592
SOFTWARE = "Python"
SOFTWARE_VERSION = "2.7.17"
DEFAULT_STATE = False

In case the value will change inside the code, you should use a simple variable instead of a constant. The advantages are that you can easily change the value of these constants without searching in the code and you can see in the code what this value actually means. If there are hard-coded values, these numbers do not always make sense. Compare the following:

if voltage_mV > 1.2:
    print("Enough power left!")
if voltage_mV > MIN_VOLTAGE:
    print("Enough power left!")

The second version of that code is more likely to make sense to the reader than the version with hard-coded values.

When using constants, they are usually stored at the top of the document so they are easier to find. An alternative to constants would be to use a configuration file which would be more advanced already. Common formats for config files are JSON (JavaScript Object Notation) format or YAML (Yaml Ain’t Markup Language) format.

Comments

The use of comments in code is often discussed among developers. Why using them? Why not using them? When to use them? There is no absolute answer to this topic, but here are some guidelines that you can apply.

Avoid comments

Don’t understand this wrongly. Comments are great to describe the functionality of your code, but you should still try to limit the number of comments that you insert into your code. Why? Because it clutters the code and it can be misleading. Why can it be misleading? Well, the code is the part that actually does what is says, so if there is a change in the code, the code will still tell the truth, but when the code changes, the comments are often untouched. This means the comment tells something different as the code executes. In that way, the code can lie.

Have a look at the following example:

if light_sensor_mV > 300:
    # sets day_time to True
    is_day_time = True
else:
    # sets day_time to False
    is_day_time = False

Now it can occur, that you may change the sensor, or make a different circuit which then inverses the behavior of the sensor. You change your code so it does work properly again. But as you only verify if the code is correct, you may not realize that the comments are not matching what the code does:

if light_sensor_mV < 300:
    # sets day_time to True
    is_day_time = False
else:
    # sets day_time to False
    is_day_time = True

The issue is, that when you re-visit your own code after several months, you forget that you changed the code and then you only see that the comments and the code do not match. What does that mean? is the code wrong? Do you need to change the code? So you can now either trust the comment and change the code or you can trust your code, not knowing if it worked or not, and change the comment. This creates some ambiguity that is unnecessary and can easily be solved by removing these comments. There, it helps that your variable names and function names have a good descriptive name so that you don’t need any comments to figure out what they mean.

To make it short: Comments can lie, code cannot lie.

Write meaningful comments

But this does not mean you should never use any comments. The goal is to avoid unnecessary comments. A comment is appropriate if it adds value to the code.

There are situations where a code is simply not necessary:

# set radius to zero
radius = 0
# set center to (0, 0)
center_x = 0
center_y = 0

def print_hello():
    # prints "Hello"
    print("Hello")

The above code contains comments that are totally unnecessary. Except from the risk that the comments could be misleading, these comments here do not add value. Some developers would go so far to say that, if you have the urge to write a comment to explain the code, the code itself is not well written. I would not go so far, but I would recommend thinking if that comment is really useful or not.

Comments that can add value include explanation why you set a certain value, explain the general functionality of a complex function or add notes to remind you to add a new feature later. Some text editors even highlight the word TODO for that matter.

max_battery_voltage_mV = 3300
min_battery_voltage_mV = 2700
battery_voltage_mV = voltsensor_battery.read()

# check if voltage is enough to drive back
if battery_voltage_mV > min_battery_voltage_mV:
    # TODO: implement functions to continue
    print("Continue driving!")
else:
    # TODO: implement functions to drive back
    print("Driving back!")

You can see that the above code has a comment explaining why there is a check of the voltage and there are comments explaining what needs to be done in the near future. When these features are implemented, these TODO comments should be removed.

Before writing a comment, think whether it adds value to your code or not.

Commenting code

When testing your code or testing a new feature, you will likely comment out some code to see how the program reacts to these specific lines. This is totally fine while developing, but when there are still lines of code that are commented out after you finished your work, this will also lead to confusion. People will most likely not use this code as it is not needed, why else would it have been commented out earlier? At the same time, people will not remove this code, because why else was this code not deleted earlier? This code might be needed later? Maybe not? Other people don’t know. And you also will nit remember after a longer break.

Some people recommend to remove the code as soon as you don’t need it anymore, to avoid this confusion. By using a version control system like git, the code that has been removed is never really lost anyways. Other developers don’t like the idea of radically removing code, they might need it later. So if you want to keep the code ready to re-integrate again, add a comment explaining why you put that code into comment. Yes, this will clutter the code even more, but at least there is no confusion about why there is code into comment.

When adding some test code such as additional print statements, you may also add a comment to explain that this code is for debugging only. This makes it easier to clean up your work when it is finished and working.

Use functions

This might sound obvious, but sometimes people tend to forget to implement some specific code as a function. This might happen, because the code is just a few lines that are repeated and implementing a function seems a little bit too ambitious. Then, you want to change that code and then you need to change it several times inside your code. If this code was implemented in a function, you could easily change it one single time and then it would be fine again.

For example if you want to calculate the distance between two points:

from math import sqrt

point1 = (2, 5) # in cm
point2 = (3, 7) # in cm
distance1 = math.sqrt((poin2[0] - point1[0])**2 + (poin2[1] - point1[1])**2)

point3 = (5 -2) # in cm
point4 = (0, 3) # in cm
distance = math.sqrt((point4[0] - point3[0])**2 + (point4[1] - point3[1])**2)

In the above code, the coordinated of the points are indicated in centimeters but you want to have the distance in meters. You now need to add the conversion twice:

from math import sqrt

point1 = (2, 5) # in cm
point2 = (3, 7) # in cm
distance1 = math.sqrt((poin2[0] - point1[0])**2 + (poin2[1] - point1[1])**2)
distance2_m = distance1/100 # add conversion here

point3 = (5 -2) # in cm
point4 = (0, 3) # in cm
distance = math.sqrt((point4[0] - point3[0])**2 + (point4[1] - point3[1])**2)
distance2_m = distance2/100 # add conversion here

If you implemented a function for this, you would simply need to add the unit conversion once and the actual code would be much cleaner:

from math import sqrt

def calculate_distance(point1, point2):
    distance = math.sqrt((poin2[0] - point1[0])**2 + (poin2[1] - point1[1])**2)
    distance_meters = distance/100
    return distance

point1 = (2, 5) # in cm
point2 = (3, 7) # in cm
distance2_m = calculate_distance(point1, point2)

point3 = (5 -2) # in cm
point4 = (0, 3) # in cm
distance2_m = calculate_distance(point3, point4)

The function does not much more than the actual code, but it is easier to maintain. You can simply add or remove parts of the function and then the changes are consistent for each time you use this function. Functions are a powerful tool, so don’t hesitate to use them. Also, using functions allows you to use a meaningful name that might be more descriptive than the actual formulas used in the code.

Separate different levels of abstraction

What is a level of abstraction? When writing code, you might realize that some code is more abstract than other code. By abstract, I mean that some code is closer to the actual hardware than other pieces of code.

For example, if you have a micro-controller and you change the state of one pin from LOW to HIGH, this would be considered very low level behavior as this is very close to the real hardware and is not abstract at all.

Instead, if you use a function that simply sends a message through the serial port or just use the print() function, this is considered to be very abstract code as you do not need to worry which bits and bytes are changed in order to make your message appear on the screen or on another device. This code would be very abstract and it would probably be implemented through a set of lower level functions that do the work for you.

motor_enable_pin = True # low level code
print("Motor enabled!") # high level code

The actual advice is to keep same level of abstraction together. No high level code together with low level code in one function. How to do that? Implement a function that makes an action more abstract. In the above example, there is the changing of the voltage level of a hardware pin next to a print statement. In this case, you may want to re-organize the code as follows:

def main():
    enable_motor() # high level code


def enable_motor():
    enable_motor_pin() # mid level code
    print("Motor enabled!") # mid level code


def enable_motor_pin():
    motor_enable_pin = True # low level code

Now, in the above code, there are three functions that are responsible to enable the motor and to write a message on the screen. You may not find this very intuitive as you actually write much more code which is not doing more than the two lines earlier, but in the end, the code is more structured and easier to read.

In the end, you could say that, by writing more, you end up reading less. You only need to look at the abstraction level that you actually are interested in. If you only want to make sure the message you write on the screen is correct, you do not need to worry about which pin has been enabled or which other actions have been taking in order to enable the pin. Of course, the above example is very short. In a real world example, each function will contain more code and then the use of functions of different abstraction levels will make even more sense.

Keep it clean

Avoid the “Quick and Dirty” approach as you probably end up rewriting it several times and then spend more time as if you had done it the “Nice and Clean” way.

By “Quick and Dirty”, I mean to ignore all the advice above just because it is too much work to think of good variable and function names and not putting any value adding comments because “you know what it does”. You might end up with code that will be used more often than you could imagine and you end up wondering why this code is such a mess.

I once was told to write some code the “Quick and Dirty” way because the code should do a small task and then I would not have to bother with that code ever again. I ended up working over 8 month with that same code and I wished I just made it “Nice and Clean” in the first place instead of trying to improve it each time that I re-open the code.

If you are not sue about certain topics, you can go back to Object-Oriented Programming.

Object-Oriented Programming

You can see Object-Oriented Programming (OOP) somewhat like a more advanced topic in Python where Python does implement it pretty well. Even though Python does not require an OOP approach for making complex programs, it sometimes makes life easier while sometimes it doesn’t. As you will most likely get in touch with some code that has been written in an Object-Oriented approach, it is at least good to have seen the concepts of it.

While Python scripts work pretty well without any implementation of Object-Oriented Programming, it allows you to keep your code even more structured in form of Classes, Attributes and Methods.

Classes

In simple terms, a Class is a group of related variables and functions, all bound to a so-called Object. Except, in the scope of OOP, you call the variables Attributes and the functions are called Methods. These objects are meant to behave similarly to real-world objects with individual properties and things that can be done with them. The syntax of creating a class is fairly easy:

class Robot:
    # remaining code here

As you can see, the class is introduced with the keyword class. Next, there is the name of the class, in this case, it is called Robot. By convention, class names are written with the first letter as a capital letter, the remaining lowercase. If the class name is a compound word such as “Car Battery”, you would write each word starting with a capital letter: CarBattery.

Then, there is another “keyword” called self. In this case, self is not really a keyword of Python as you could call it whatever you like, but the word self has become a convention. In other languages like Java, the fixed keyword would be this where you cannot change the keyword there. In python, you could call it john, roboshack or whatever you like. Sticking to the convention of self isn’t a bad idea though.

The final thing to mention about the class declaration is that it ends with a colon and the following code, that is belonging to the class, is indented, similar to an if-statement or a loop.

Attributes

As already mentioned, the Attributes are variables that belong to the class object, just like the properties of a real-world object. A robot for example can have a name, a number of wheels, a number of robotic arms, and battery voltage. These Attributes would then be introduced as such:

class Robot:
    name = "Sony"
    nr_wheels = 4
    nr_robotic_arms = 0
    battery_level_volt = 12.3

These Attributes are easily accessible within the function, but also outside of the function. Notice that these attributes are still part of that Object and do not behave in the same way as a global variable. (The scope of variables will be covered later in another part.)

Before you learn how to actually use this Class, let’s have a look at the last part: the Methods.

Methods

A Method is basically a function that is tied to an Object of a Class. The Method can easily make use of its Class Attributes, again through the self keyword. One special thing is, that a Method always takes at least one argument: self.

class Robot:
    say_hi(self):
        print("Hi!")

The above Method doesn’t make use of its Attributes, but the following for example does:

class Robot:
    battery_level_volt = 12.3
    battery_is_full = True

    check_battery(self):
        if self.battery_level_volt > 12.2:
            print("Battery is still charged!")
            self.battery_is_full = True
        else:
            print("Battery is getting empty!")
            self.battery_is_full = False

As you can see, the above example is not receiving the actual battery voltage as an argument and yet it can access the battery status through the self keyword. This can help to unclutter functions that would otherwise take quite a lot of input arguments to work with. If you still want to pass some arguments, you can simply add them after the argument self, separated with a comma:

class Robot:
    battery_level_volt = 12.3
    battery_is_full = True

    battery_charged(self, battery_voltage):
        if battery_voltage > 12.2:
            print("Battery is still charged!")
            return True
        else:
            print("Battery is getting empty!")
            return False

The latter solution is less elegant as it requires additional input and it will give some additional output that might be stored in the battery_is_full Attribute anyways. Additional input parameters make more sense when they contain information from outside of the Class such as a distance to travel or sensor data.

Instances

When you create a new variable of the type of a Class, you actually create an Instance of that Class. You can even make several Instances of the same class. This happens as such:

class Robot:
    name = "Sony"
    battery_level_volt = 12.3
    battery_is_full = True

    check_battery(self):
        if self.battery_level_volt > 12.2:
            print("Battery is still charged!")
            self.battery_is_full = True
        else:
            print("Battery is getting empty!")
            self.battery_is_full = False

my_robot = Robot()
my_robot.name = "Turbo"
my_robot.battery_level_volt = 13.1
my_robot.check_battery()

The above code will create an instance of a Robot and then save some data into its Attributes and even call a Method of that class. Now, assuming that the class is already given as before, the following example would create two more instances of that Class:

robot1 = Robot()
robot1.name = "Nitro"
robot1.battery_level_volt = 12.7
robot1.check_battery()

robot2 = Robot()
robot2.name = "Speedy"
robot2.battery_level_volt = 12.0
robot2.check_battery()

As these two robots above are different instances of the same Class, they both have the same Attributes, but they have different values for their Attributes. This keeps things well organized and reduces the risk to override the properties of one object when handling another object. The Method robot1.check_battery() will only check the battery voltage of robot1 and it will not bother about robot2. This is done by using the self.battery_level_volt Attribute inside the Method definition. It will only check the voltage of its own robot.

Constructors

There is one special type of Method, the so-called Constructor of the class. It is actually called when you type:

my_robot = Robot()

The above line calls the Constructor of the Class. This means that there, Python creates all the Attributes and Methods related to that Object. In addition, this Constructor is able to call a special internal Method with the name init surrounded by two underscores:

class Robot:
    name = "Sony"
    battery_level_volt = 12.3
    battery_is_full = True

    __init__(self, name, battery_voltage):
        self.name = name
        self.battery_level_volt = battery_voltage
        self.check_battery()

    check_battery(self):
        if self.battery_level_volt > 12.2:
            print("Battery is still charged!")
            self.battery_is_full = True
        else:
            print("Battery is getting empty!")
            self.battery_is_full = False

my_robot = Robot("Ronny", 12.4)

Now, when the Constructor Robot(“Ronny”, 12.4) is called, it will automatically call the __init__() Method and it will set the name and the battery voltage to the parameters given to the Constructor. In addition, it will call the check_battery() Method. This can save some work to initialize the object or take some actions that you would otherwise do anyways right after creating a new Instance of that Class.

Inheritance and Polymorphism

Just like in other programming languages, Python classes can be based on other classes. Also, you can overwrite existing methods and overload functions. These topics will not be explained in this guide as they are already more advanced and they would make things even more complicated as they already are at this point. For simple Python programs, and even simple scripts for robotics programming, these concepts are not really necessary at this point in time.

Okay, but why to use Classes?

If you think, this looks more complicated than simply making a bunch of non-OOP variables such as in the following example, you will be proven wrong:

robot1_name = "Nitro"
robot1_battery_level_volt = 12.7
battery1_is_charged = robot_check_battery(robot1_battery_level_volt)

robot2_name = "Speedy"
robot2_battery_level_volt = 12.0
battery2_is_charged = robot_check_battery(robot2_battery_level_volt)

Keeping things organized

At the first sight, the above might look easier to implement. But one big disadvantage simply is, that these are all loose variables. This means if you want to make two instances of the same object, without using a class, each property has its own variable. If you want to copy all the parameters into another variable, you need to specify every single variable. The same holds true if you want to pass all these properties as a parameter into a function, you will need to enter each variable name and while doing so, for each variable you are potentially prone to make a typo.

Without using a class, you will be likely to do the following if you want to copy all the values from one instance to another one:

robot1_name = "Nitro"
robot1_battery_level_volt = 12.7
battery1_is_charged = robot_check_battery(robot1_battery_level_volt)

robot2_name = robot1_name
robot2_battery_level_volt = robot1_battery_level_volt
battery2_is_charged = battery1_is_charged

To be fair, you could do it in one single line in Python:

robot2_name, robot2_battery_level_volt, battery2_is_charged = robot1_name, robot1_battery_level_volt, battery1_is_charged

The one-line solution does work, but it doesn’t make it more readable in the case you have many variables. This will be more elegant if you’d just implemented a class containing all the properties and attributes you need:

robot1 = Robot("Nitro", 12.4)
robot2 = robot1

Now, your code has copied every class attribute from robot1 into robot2 without a high likelihood of making a typo, no risk to forget one variable and in a very readable way.

Keep things inside a class

Another advantage of implementing a class is that each method has easy access to its class attributes through the self specifier. Also, you can still access the attributes from outside the classes (in Python, you cannot make a variable private or protected like in Java or other languages). As a result, you can make the code easier to read and write as you don’t need to specify every attribute as a parameter for a method call. You just access the data from within the method. In the same way, you can modify the class attributes without the need of having a return value.

So instead of the following code:

robot1_name = "Nitro"
robot1_battery_level_volt = 12.7
battery1_is_charged = robot_check_battery(robot1_battery_level_volt)

You simply have the following code in the OOP approach:

robot1 = Robot("Nitro", 12.7)
robot1.robot_check_battery()

The Object-Oriented code is shorter, easy to read and modify and it does not contain any unnecessary information. The complexity is more or less hidden inside the method definitions which is fine as you only write the method once, but you can use it as often as you want.

Modularity

Another important aspect of Object-Oriented Programming is the modularity of the code. You can easily write an external file containing the class definition and then import it to the Python file and make use of the class. This makes the source code more structured and at the same time, you can easily copy the file containing the class definition into other projects. For example, if you use import rospy, you do exactly that, you import the file containing class definitions for ROS.

Use external code

Even though you may get along pretty well without using classes, you might receive code from a colleague or you find an example on the internet and it has been implemented using OOP. In this case, it is still useful to be familiar with the OOP approach of programming in Python as it is a widely used approach. After all, OOP is there to make things easier. Even though you might want to avoid that initial hurdle to understand how it works, it will probably get to you in any unexpected way.

This is it! Now you know a wide basis of the Python programming language. There are more aspects that have not been covered in detail or not at all. But the parts that have been discussed so far should make it easier for you to get started.

Continue learning about general python programming advice or go back to revisit functions.

Functions

After you saw how to use loops, what do you use when you want to repeat the same code several times? Sure, a loop! But what if you want to run the same code, but only once every now and then? Just copy the code every time you want to run that code? Why not use a function?

What are functions?

A function can be seen as a mini program inside your program. They are very useful to avoid coping and pasting the same blocks of code over and over again. The difference to a loop is that a function is usually used to group some functionality together and repeat the same algorithms at different places inside your program instead of repeating it several times after each other like a loop does.

You probably know the concept of a mathematical function such as: f(x) = x². This kind of functions can also be used in programming:

def f(x):
    return x**2

The above function is representing the function f(x) = x². The key word def is used to indicate that there will be a new function definition. After the key word def, the name of the function is defined, in this case simply ‘f’. Inside the parentheses is the parameter that can be given to the function. This is a way to give your mini program some input. A function can have several input parameters, they are then separated by a comma. Then the definition ends with a colon and the next lines that are indented. Everything inside the same level of indentation is part of that function definition. The last element of the function is the return key word. The variable(s) after the return key word are given as an output of your function.

You can call the function as follows:

y = f(3)

Now, the return value will be stored inside the variable y. Like this, a simple formula such as a²+b²=c² could be easily implemented as:

a_squared = f(a)
b_squared = f(b)
c_squared = a_squared + b_squared

In case you as: Why using a function for such a simple thing? Well, you probably will not use it for such a thing. Most likely, you will use it for more complex calculations. Also, you will hopefully not call the function ‘f’ and the input parameter not ‘x’. Let’s have a look at another function. The following will calculate the surface area of a rectangle with two input parameters:

def rectangle_surface_area(height, length):
    return height * length

The above function has a descriptive name, and the parameters are explicitly telling what they are supposed to be. This is often seen as good practice for writing functions.

A function can be more than a simple line of calculations. It can even contain print statements, loops and even other functions.

Small Example

The following function will ask the user to input his name and his age.

def ask_personal_data():
    name = input("Please enter your name: ")
    age = input("Please enter your age: ")
    return name, age

The function will then be used as such:

user_name, user_age = ask_personal_data()

if user_name == "John":
    print("Hello John! Did you know that this is a very common name?")
else:
    print("Hello!")

As said, the above function will ask the user to type the name and the age and then the function returns both values. The user will see something like this:

Please enter your name:
Jacob
Please enter your age:
25

Advantages of functions

With this function, you can now use it everywhere in your code to perform the same tasks again and again where they are needed. One advantage is that they make the program sorter and easier to read. Another advantage is, that you only need to modify the code one single time instead of modifying it on each occasion where you use the same algorithm.

Increased Maintainability

Let’s have a look at the following example of a driving robot:

motor_speed = 0.2 # m/s

sensor_input = 540 #assume: 0 -> 0 meter and 1024 -> 2 meter
distance_to_wall = sensor_input * 2 / 1024
if distance_to_wall <= 0.2:
    print("You need to stop now!")
    motor_speed = 0

# read user input

sensor_input = 370 #assume: 0 -> 0 meter and 1024 -> 2 meter
distance_to_wall = sensor_input * 2 / 1024
if distance_to_wall <= 0.2:
    print("You need to stop now!")
    motor_speed = 0

# calculate battery voltage

sensor_input = 210 #assume: 0 -> 0 meter and 1024 -> 2 meter
distance_to_wall = sensor_input * 2 / 1024
if distance_to_wall <= 0.2:
    print("You need to stop now!")
    motor_speed = 0

Now let’s see how this could be reduced with a function:

def calculate_distance(sensor_input, motor_speed):
    sensor_input = 540 #assume: 0 -> 0 meter and 1024 -> 2 meter
    distance_to_wall = sensor_input * 2 / 1024
    if distance_to_wall <= 0.2:
        print("You need to stop now!")
        motor_speed = 0
    return motor_speed

motor_speed = 0.2 # m/s

motor_speed = calculate_distance(540, motor_speed)

# calculate battery voltage

motor_speed = calculate_distance(370, motor_speed)

# read user input

motor_speed = calculate_distance(210, motor_speed)

Now you might ask: where is here an advantage in number of code lines? The first option only has 4 lines more than the second option. Yes, this is correct, the difference is not that big indeed.

However, imagine you decide not to use the distance in meters as you will always get small decimal values but instead, you want to use centimeters instead. In the first option, you need to modify the calculation of the distance three times. In the second option, you only need to do it once. Also, if you use the same calculations 20 times inside your program, you don’t always think of each time you used it and you might forget it at one spot and then there are mistakes in the code. Or imagine you have a different sensor with a range from 0 to 2048 bits instead of 1024. This means many changes all over the code or only one single change in the function.

Increased Readability

Not convinced yet? The following function will not save you many lines, but the calculation inside is not very self-explanatory. The function name will tell you more about what you are calculating.

# import the square-root function from the math module
from math import sqrt

def distance_between_points(x1, x2, y1, y2):
    distance = sqrt((x2-x1)**2 + (y2-y1)**2)
    return distance

Functions can also be used simply to structure your code into logical chunks or to give meaningful names to the algorithms that you program, even if you know you will use this algorithm only once in this program.

Modularity

Also, you might notices the line from math import sqrt. This line imports a function from another python file, so you can use it in your code as well. This means functions make the software more modular and reusable. There are many more functions that you already saw like the input() function, the print() function (yes, technically in Python 2, the print function is not a real function but a statement but in Python 3 it is a function) and here the sqrt() function.

So you have been using functions all along until now, without really noticing it. And this is why people use functions, to make their lives easier and keep the complexity hidden in other places. You can define the functions inside the same file as the main program or in a separate file and then import the functions from this file.

Importing functions from other Python files (so called Python modules) allow developers to make an Artificial Intelligence (AI) application with less than 50 lines of code. The total code behind this can then be several thousands of lines, but the part that the developer is writing is only a few lines long.

Additional Notes

Using functions can make life easier, even though they appear more difficult at first. One thing you need to consider is, that they require an input and an output in case they should modify a value such as in the example above:

motor_speed = calculate_distance(210, motor_speed)

The example without the function could always use the motor_speed variable while when sing functions, you need to hand this variable over to the function as a parameter as otherwise, there will be an error that the function is not defined. This is due to the fact that variables can have different scopes. Also, if you want to change the value of a variable, you need to return it as otherwise the actual variable has not been changed. When calling a function with input parameters, the functions makes a copy of the variable values and does not change the original variables. Again, this has also to do with the scope of the variables. Later, you will learn more bout variable scopes.

In case you want to dive even deeper into the topic of functions, have a look at the article Concise Notes on Functions in Python.

Continue learning about Object-Oriented Programming (OOP) in Python or g back to revisit loops.

Loops

When you write code, you might notice that you need to execute some code several times in the same or in a similar way. This could be because you need to check the value of some variables or because you do the same thing for all elements of a list.

Why using Loops

Here are some examples of code snippets that would repeat the same action (or similar).

names = ["Thomas", "James", "Nicolas"]

print("Hello, " + names[0] + "!")
print("Hello, " + names[1] + "!")
print("Hello, " + names[2] + "!")

if (names[0] == "Henry"):
    print("One of these gentlemen is called Henry Ford, correct?")
if (names[1] == "Henry"):
    print("One of these gentlemen is called Henry Ford, correct?")
if (names[2] == "Henry"):
    print("One of these gentlemen is called Henry Ford, correct?")

The examples above are typical code lines that are repeated over a certain number of times. The first example is printing the same text with different names while the second part if comparing different entries one after the other as a sequence. For 3 entries, this is no issue, but what if there is a list with 100 entries? Or even 1000? Would you then copy the same code 1000 times? Or even worse, what if the exact number is not known in advance? How to know how many times to repeat the code?

The solution for this issue would be to use loops. A loop is repeating the same thing several times and is made of several key elements: the starting point, the end point, the step size between the iterations and the code that is actually executed.

There are two types of loops: for loops and while loops. The for loops are mainly used for repeated actions with an end point after a predefined number of so called iterations. An iteration is the single execution of the code that will be repeated. So each time the same code is running another time, a new iteration is being executed. The while loops often end due to an external (or internal) condition that is often independent from the exact number of iteration. This is often used to stop a loop after an event like a button press is triggered or when a certain measurement value has been reached. Sometimes, a while loop is used to replace a for loop but to have even more control about the end point or the step size.

For Loops

The syntax of a for loop is as such:

for i in range(start_point, end_point, step_size):
    print("Execute code here several times!")

Note that the letter i here is often used to indicate the iterator, which is a variable that changes for each iteration based on the starting point, the end point and the step size. The iterator starts with the value defined as the starting point and it will end with the value defined as the end point. Then, it will increase (or decrease) after each iteration with the value defined as the step size. Also, note that the code that should be executed is indented (shifted to the right compared to the line above) to indicate which lines belong to the loop and which are not. (This is actually very similar to the conditional statements.)

The printing example at the top can therefore be written as follows:

names = ["Thomas", "James", "Nicolas"]
for i in range(0, 2, 1):
    print("Hello, " + names[i] + "!")

The code above will start with i=0 and it will increase i by the number 1 after running the code for the first time, then it runs a second time with i=1 and the iterator will be increased again. Then it runs the code again with i=2 and after that it stops. The variable i in the print statement is used as the index to indicate which name of the list we want to print.

The second part of the example can also be replaced by a loop structure combined with a nested conditional statement:

names = ["Thomas", "James", "Nicolas"]
for i in range(0, 2, 1):
    if name[i] == "Henry":
        print("One of these gentlemen is called Henry Ford, correct?")

The code above will iterate through each name in the list and check if it is equal to the value “Henry” and if it is, it will print a message. The advantage now is, that the code can easily be adapted if the list of names will be bigger.

Just like nested conditional statements, you can have nested loops. They can be either nested inside another loop, or nested inside a conditional statement. For example you could have an if-statement and inside this statement, you could have a loop.

iterations = 4
if iterations > 1:
    for i in range (1, iterations, 1):
        print(his is iteration number " + i + "!")

A very interesting case for nested loops inside loops would be if you want to go through a 2D matrix for example:

points = [[1, 3], 
          [1, 5], 
          [3, 1],
          [2, 2]]
for i in range(0, 3, 1):
    for j in range(0, 1, 1):
        points[i][j] = 0.0

The code above will set every element inside the given 4×2 matrix to 0.0 as it goes through both loops. This is often used for vision application to go through an image matrix.

The for loop can not only be used to go through a range of numbers, but it can also be used to go through a list directly. So instead of using for i in range(0,2,1) you can also write for i in names which will then go through each element of the list and execute the code. This is especially useful when the length of the list is not known and this is a very elegant way to solve this. (It is also considered the Pythonic way to use the for loop.)

names = ["Thomas", "James", "Nicolas"]
for name in names:
    if name == "Henry":
        print("One of these gentlemen is called Henry Ford, correct?")

Note that the example above uses a different name for the iterator as i is not very descriptive. Also, you now don’t use names[i] but the variable name used for the iterator name.

While Loops

While loops are slightly different and probably less obvious. They can perform the same task with some more manual intervention than a for loop. Just like the for loops, a while loop also has a starting point and an end point. The starting point is the first time that the loop is being executed. The end point is the last time that it is being executed. Unlike for loops, there is not necessarily a fixed amount of iterations that the while loops are running through. The loop ends after a certain condition is not fulfilled anymore. The only explicit parameter that a while loop has is a condition that is specified at the beginning. This condition must have a boolean state, either true or false.

The syntax is as follows:

condition = True
while (condition == True):
    print("Execute code here several times!")

The above loop will run forever. This is because there is no possibility that the condition that is checked before each new iteration will ever change its state. As a result, the condition will be always true and the loop will never end.

You can see now why some people recommend to avoid using while loops as they have the possibility to result in an endless loop. The result is a dead lock, a state that can’t ever be left.

Also you can see, why the while loop is less obvious as it requires implicitly some additional elements that are not explicitly required by the syntax such as a way to end the loop if necessary and possibly some way of keeping track of the number of iterations. For a better understanding, you can recreate the functionality of a simple for loop as well:

iterator = 0
end_value = 5
while (iterator < end_value):
    print("Execute code here several times!")
    iterator = iterator + 1    # change the number of iterations

The code snippet above shows how you can recreate a for loop manually. Sometimes, this gives you more control how the loop should react. Most of the time, though, you will not use while loops with a counter, rather you will use it with some kind of check to compare boolean values, string values or number values which will change the loop condition so the loop will finish. For example when a button is pressed, an electro-mechanical state of a sensor has changed or if the user has given a certain input.

The following example will ask the user to input his password and then check if it is correct. It will do it an infinite amount of time as long as the input that has been given is incorrect:

password_is_correct = False
password = ""
while (password_is_correct == False):
    password = input("Please enter the correct password! ")
    if (password == "123456"):
        password_is_correct = True
    else:
        password_is_correct = False
        print("Wrong password: Access Denied! Try again!")
print("Hurray, the password was correct!")

Sometimes, an infinite while loop is exactly what you want. This is rather common for the main program of a piece of software as it should not just stop but keep going forever. This is generally true for robotic systems that often use while loops to describe their main program and then run forever until they are shut down or run into an error. In this case, shutting down the loop means to change its input condition to false. However, there is another way to leave a loop without changing its input condition: the break keyword.

Breaking a loop

The break keyword can be used to exit the loop immediately. This means that the loop will not reach its end but it will exit at the spot where the interpreter encounters the break statement. Leaving the loop with a break is usually done in combination with a conditional statement.

Here is an example on how to use the break statement:

password_is_correct = False
password = ""
counter_trials = 0
while (password_is_correct == False):
    password = input("Please enter the correct password! ")
    if (password == "123456"):
        password_is_correct = True
    else:
        password_is_correct = False
        counter_trials += 1
        print("Wrong password: Access Denied! Try again!")
    if counter_trials >= 3:
        print("Maximum of trials reached, locking the device!")
        break
    print("End of this loop has been reached successfully!")

The example above shows a simple algorithm that asks for a password by using a while loop, just like in an example before. In addition to that, there is a condition that the password may not be more than three times incorrect. In this case, the loop will exit through the break statement. This simple case could also have been resolved by simply changing the variable “password_is_correct” to True, but this would have three major consequences:

  1. The variable “password_is_correct” would have a value that does not match the situation.
  2. The program would need to run code (here the input condition validation) even though this is not necessary.
  3. The rest of this loop would still be executed, even though this might not have been the intention.

When to use a break often depends on the intentions of the program and how the loop should be exited. Often, there are several possibilities to obtain the same result.

There is also a way to skip parts of the loop, this is done with the continue statement.

Continue

In some cases, you don’t want the loop to be finished at a certain point, but you also don’t want it to go through the rest of the code inside the loop. A break would be interesting to use but nit quite do the job in this case. The continue statement will make the loop skip the remaining part of the code and then continue in the next iteration.

Consider the following example where the loop simply goes through numbers 0-19 (20 is excluded) and prints the odd numbers and skips the even numbers.

for counter in range(20):
    if (counter%2==0):
        continue # skip even numbers and continue loop
    print(counter) # will not be printed if counter is even

The example above will continue with the next loop iteration after it reaches the continue statement, this means that the print statement will not be executed.

Loops are for many applications very useful, especially when you need to repeat code several times. While loops are especially useful when you want to repeat an action for an undefined amount of times, for example when the exact number of iterations is not known yet, or if there is no fixed number of iterations. Loops are a critical element for robotic applications too. When a robot needs to move forwards until it approaches a wall in front, the while loop condition will be based on the state of a sensor of the robot. There are many more examples, different for each situation.

Another interesting way to avoid the repetition of code is the use of programming functions.

Continue learning about functions or go back to revisit conditional statements.

Conditional Statements

What are conditional statements? Basically, it expresses code that is being executed only when a certain condition has been fulfilled. They are also known as “IF statements“. It can be explained by the following sentence: IF this has happened, DO the following, ELSE, do the other thing.

The keyword IF is being used to define the condition, followed by the action that needs to be done. It is possible to define an alternative action in case the condition is not fulfilled. The alternative action is defined after the ELSE keyword. The syntax in Python is as follows:

if <condition>:
    <primary action>
else:
    <alternative action>

Please note that the above code example contains pseudo-code inside angle brackets (<>). Pseudo-code is a common practice to show the idea of the code without implementing real functioning logic. Also, always make sure the condition is ended with a colon (“:”). This tells the computer that the definition of the condition has ended and the definition of the actual code starts. Omitting the colon will result in errors.

With the help of this conditional statement, it is possible to change the flow of the program, depending on the state of some event or element. The if statement can be divided into two major parts: the condition and the actions that are executed.

The condition can be a simple variable or an expression. The condition will be checked whether it is True or False. This means that the type of the condition is a boolean. So, the condition can be a variable in which a True/False value has been stored or it can be the result of an expression like a comparison or even the result of multiple comparisons combined with the keywords AND or OR. Here are two examples:

battery_is_full = True

if battery_is_full:
    <primary action>
else:
     <alternative action>

sensor_left = 0.2
sensor_right = 0.5

if (sensor_left < 0.1 AND sensor_right < 0.1):
    <primary action>
else:
     <alternative action>

The AND and OR keywords are elements for binary logic that will verify the state of several boolean states combined. The OR will result in True if at least one of the elements is True and the AND will result in True if all elements are True.

The next part is the action that should be executed when the condition is true. This can be any type of code. The only thing that you need to pay attention to is that the code representing the action must be indented. This means that there are some white spaces on the left side of the code compared to the line indicating the condition. This is to represent that the code is inside a code-block. These blocks are essential for the computer to understand which part still belongs to the actions under the if statement and which excluded from the if statement. This means that the blocks are necessary for the logical structure of the program, but also they keep the code organized. It is rather simple to see which part belongs to the action under the conditional statement and which does not. The non-indented code will then be executed regardless of the condition.

It is even possible to have another if statement inside the action part of an if statement. This procedure is called nesting. A nested if statement would be as follows:

battery_is_full = True
battery_voltage = 4.8

if battery_is_full:
    if (battery_voltage > 4.5):
        <nested action>
    else:
        <alternative nested action>
else:
    <alternative action>

Nesting blocks inside each other is a very common technique to make more complex decision algorithms. This can lead to some difficult to read and difficult to understand code which is more likely to contain logical mistakes. You could try to break the nested structure into smaller pieces:

battery_is_full = True
battery_voltage = 4.8

if (battery_is_full AND battery_voltage > 4.5):
    <primary action>
else:
    <alternative action>

if (battery_is_full AND battery_voltage < 4.5):
    <primary action>
else:
    <alternative action>

In the above example, there is no nested structure anymore, making the code a little more readable. However, there are some disadvantages. What would happen if the battery_is_full variable would be False? Then, the conditions for both if statements would be false and for both cases, the code under the ELSE part would be executed even though in the nested part, there was only one ELSE that would be executed. To differentiate the cases where battery_is_full = False would be covered, you would need to have four conditional statements under each other. You see, the code would be more explicit, but also more lines of code and more to type in order to cover the same logic. Also, you can see that the conditions are now more complex than with the nested structure. These are some things that you might want to consider before writing the code.

The final part of the conditional statement is the alternative code that is being executed. It is defined after the ELSE keyword followed by another indented block of code. This block defines the code that is being executed if the condition is False and the first code block is not executed. This part is not mandatory and can be skipped.

battery_is_full = True
battery_voltage = 4.8

if (battery_is_full AND battery_voltage > 4.5):
    <primary action>

if (battery_is_full AND battery_voltage < 4.5):
    <primary action>

There is one last thing that can be used to make the code more efficient. In the case above, the two conditional statements are verified even though they are mutually exclusive, meaning if one is true, the other can’t be true as well. This means that the second statement is obsolete when the first one is already being executed. (To be fair, for the example above, this makes no difference, but when the conditions are getting much more complex and there are many more situations to verify, this can be time-consuming for the computer.) To avoid this, there is a combination of IF and ELSE that helps you combining the advantage of a nested structure with the advantage of a flat code structure: ELIF. The ELIF part can be used between the IF and the ELSE part of the conditional statement. There can be as many ELIF parts as you need. The ELIF keyword is followed by another condition, just like the IF keyword in the first part.

battery_is_full = True
battery_voltage = 4.8

if (battery_is_full AND battery_voltage > 4.5):
    # battery is full
    <primary action>
elif (battery_is_full AND battery_voltage < 4.5 AND battery_voltage > 3.3):
    # battery is still usuable
    <alternative action 1>
elif (battery_is_full AND battery_voltage < 3.3):
    # battery start to be empty, set battery_is_full to False
    <alternative action 2>
else:
    # battery is empty
    <alternative action 3>

The above structure keeps the code somehow organized and with the comments, it is rather easy to keep track of all possible statements. Also, the list of different cases can go on and on without creating a nest in a nest in a nest… There is no one-size-fits-all solution for code, but sometimes one solution has more advantages. In general, you can go for the more structured method over the more efficient method as most computers are very efficient. And the more structured the code is, the easier it is to understand, modify and maintain.

Continue to learn about loops or go back to revisit inputs and outputs.