How to Use Advanced Object-Oriented Python

In this tutorial, we are going to learn advanced concepts for object-oriented programming using Python.

Prerequisites

Writing Getters and Setters

Let’s learn about getters and setters – they’re like security guards for your robot’s information! Think of them as special functions that help you safely check and change your robot’s settings. 

Just like you wouldn’t want anyone to accidentally set your robot’s speed to a dangerous level, getters and setters help protect your robot’s information and make sure it’s changed in the right way.

First, create a file called robot_settings.py inside the following folder: ~/Documents/python_tutorial.

Define a class named Robot that will help us manage the speed and battery level, which are common attributes in robotics:

class Robot:
    def __init__(self, speed=0, battery=100):
        self._speed = speed
        self._battery = battery

    def get_speed(self):
        return self._speed

    def set_speed(self, value):
        if value < 0:
            raise ValueError("Speed cannot be negative")
        self._speed = value

    def get_battery(self):
        return self._battery

    def set_battery(self, value):
        if not (0 <= value <= 100):
            raise ValueError("Battery must be between 0 and 100")
        self._battery = value

This code snippet introduces get and set methods for speed and battery level. The setters include validations to ensure the values are within acceptable ranges, which is essential for maintaining the safety and functionality of your robot.

Next, run a simple test to see these getters and setters in action. 

Add the following test code at the end of your script:

robot = Robot()
print("Initial Speed:", robot.get_speed())
robot.set_speed(5)
print("Updated Speed:", robot.get_speed())

try:
    robot.set_battery(150)
except ValueError as e:
    print(e)

Now, run the code:

1-robot-settings

You should see the outputs corresponding to the robot’s speed updates and any error messages related to battery level adjustments.

Using Inheritance

Let’s talk about inheritance in Python, a topic we briefly covered in the previous tutorial.

Inheritance is like creating a family tree of robots. Imagine you design a basic robot with common features. Then, just like how children inherit traits from their parents, you can create new types of robots that automatically get all the abilities of the basic robot, plus their own special features. This saves you from having to write the same code over and over again.

Create a new file called robot_inheritance.py inside the following folder: ~/Documents/python_tutorial.

Begin by defining a base class called Robot that will contain common attributes and methods that any robot might need:

class Robot:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, I am {self.name}.")

Now, let’s use inheritance to create a specialized type of robot. We’ll define a class CleaningRobot that inherits from Robot:

class CleaningRobot(Robot):
    def clean(self):
        print("Cleaning in progress...")

Notice how CleaningRobot doesn’t redefine the __init__ method or the greet method but inherits them from the Robot class. It does, however, add a new method, clean, which is specific to cleaning robots.

Now let’s add this test code at the end of your script to create an instance of CleaningRobot and use its methods:

my_robot = CleaningRobot("RoboCleaner")
my_robot.greet()
my_robot.clean()

Now, run the script.

2-robot-inheritance

You should see the greeting from the robot followed by the cleaning message. This demonstrates how the CleaningRobot uses functionality from the Robot base class while adding its own unique behavior.

Practicing Polymorphism

Let’s take a look at another key concept in object-oriented programming: polymorphism. 

Polymorphism literally means “many forms”.

Imagine you have a television remote control with a “power” button that works on different TVs. Just like how different television classes (Samsung, LG, or Sony) all understand the same power command, different robot classes can share common commands too. This is polymorphism in action – we can treat different kinds of objects (like our different robot types) as if they were the same basic type (like a general “robot” class). 

For example, all our robots might understand the command “move forward,” but each robot class implements it differently: a walking robot takes steps, while a wheeled robot rolls forward. Same command name, different behaviors!

Create a new file named polymorphism_example.py inside the following folder: ~/Documents/python_tutorial.

Start by defining a base class called Robot with the following method:

class Robot:
    def __init__(self, name):
        self.name = name

    def perform_task(self):
        print(f"{self.name} performs a generic task.")

Now, let’s define two subclasses that inherit from Robot but perform tasks differently, demonstrating polymorphism:

class CleaningRobot(Robot):
    def perform_task(self):
        print(f"{self.name} is cleaning the area.")

class PaintingRobot(Robot):
    def perform_task(self):
        print(f"{self.name} is painting the wall.")

Each subclass has its own implementation of the perform_task method, tailored to what the specific robot is supposed to do.

Now add this test code at the end of your script to create instances of each robot and call their task method:

robots = [CleaningRobot("Cleany"), PaintingRobot("Painty")]
for robot in robots:
    robot.perform_task()

Now, run the script.

3-polymorphism

You should see each robot performing its specific task, demonstrating how polymorphism allows us to use a common method in different contexts.

Working with Magic Methods

Now let’s explore a concept of Python programming that can significantly enhance your coding, especially when working with classes in robotics: magic methods, also known as dunder methods. 

Magic methods are special methods that start and end with double underscores. They allow us to implement functionality that mimics the behavior of built-in types and elegantly interact with different Python operations.

Create a new file named magic_methods.py inside the following folder: ~/Documents/python_tutorial.

We’ll begin by defining a class called Robot that utilizes some common magic methods:

class Robot:
    def __init__(self, name, battery_life):
        self.name = name
        self.battery_life = battery_life

    def __str__(self):
        return f"{self.name} (Battery life: {self.battery_life} hours)"

    def __len__(self):
        return self.battery_life

    def __add__(self, other):
        if isinstance(other, Robot):
            return Robot(f"{self.name}&{other.name}", self.battery_life + other.battery_life)
        raise ValueError("Can only add another Robot")

In this snippet, we define three magic methods:

__str__: customizes the string representation of our objects, making it more informative.

__len__: allows us to use Python’s len() function to get the battery life of the robot.

__add__: enables us to “add” two robots together to create a new robot with combined names and battery life.

Now add the following test code at the end of your script:

robot1 = Robot("Robo-One", 5)
robot2 = Robot("Robo-Two", 3)
print(robot1)
print("Battery life of", robot1.name, "is", len(robot1), "hours")
combined_robot = robot1 + robot2
print(combined_robot)

Run your code.

4-magic-methods

You should see the string representations of the robots, their individual battery lives, and the details of the combined robot. 

That concludes our overview of magic methods. Thanks, and I’ll see you in the next tutorial.

Keep building!

How to Read and Write Files in Python

In this tutorial, we are going to learn how to work with files in Python. Handling files is a fundamental skill in programming, essential for tasks like logging data, saving configurations, and processing input in robotics applications.

Prerequisites

Working with Files: Create, Write, Close, Append, and Read

Let’s cover how to create, write, close, append, and read files using Python’s built-in functions. 

Creating and Writing to a File

To create and write to a file, you can use Python’s open function with the ‘w’ mode. This mode creates the file if it does not exist and overwrites it if it does.

Create a file called file_handling.py inside the following folder: ~/Documents/python_tutorial.

Write the following code:

file = open('example.txt', 'w')
file.write('Hello, world!')
file.close()

It’s important to close the file when you’re done with it. Closing the file ensures that all changes are saved and frees up system resources.

Appending to a File

If you want to add content to a file without overwriting the existing content, you use the ‘a’ mode with the open function.

file = open('example.txt', 'a')
file.write('\nAdding a second line.')
file.close()

Reading from a File

To read the contents of a file, you use the ‘r’ mode with open. There are several methods to read, such as read(), readline(), which reads one line at a time, and readlines(), which returns a list of lines.

file = open('example.txt', 'r')
content = file.read()
print(content)
file.close()

Using the with Statement

While using open() and close(), it’s safer to handle files with a context manager using the with statement. This ensures that the file is properly closed after its suite finishes, even if an exception occurs.

with open('example.txt', 'r') as file:
    content = file.read()
    print(content)

You should see the content of example.txt printed, which now includes both the original line and the appended line.

1-file-handling
2-example-txt

In robotics, file handling is important for tasks such as storing sensor readings, saving configuration settings, or logging events for diagnostics. Efficient file management can help maintain the integrity and performance of robotic systems.

That’s it for this tutorial on working with files in Python. Thanks, and I’ll see you in the next tutorial.

Keep building!

How to Handle Errors in Python

In this tutorial, we are going to learn about how to handle exceptions in Python. Exception handling is important because it helps your programs to gracefully handle unexpected situations, like errors during execution, without crashing. This is particularly important in robotics, where an unhandled error can cause a robot to stop functioning or behave unpredictably. 

Prerequisites

When Things Go Wrong: Handling Exceptions

In Python, errors detected during execution are called exceptions. These might occur, for example, when trying to read a file that doesn’t exist or attempt to divide a value by zero. Without proper handling, these exceptions will cause your program to stop and raise an error message.

Let’s set up an example where errors might occur. We’ll attempt to divide two numbers, where the divisor is zero.

Open your code editor and create a new file named exception_handling.py inside the following folder: ~/Documents/python_tutorial.

def divide(x, y):
    try:
        result = x / y
        print(f"The result is {result}")
    except ZeroDivisionError:
        print("You can't divide by zero!")

Here, the try block contains the code that might cause an exception. The except block tells Python what to do when a particular type of exception arises—in this case, a ZeroDivisionError. 

You can catch different types of exceptions by specifying them explicitly, or catch all exceptions by just using except: without specifying an error type.

Now, let’s test our function with different inputs:

divide(10, 2)  # This should work fine
divide(5, 0)   # This should raise an exception

Run the script.

You should see the output showing the result of the first division and a message “You can’t divide by zero!” for the second. This demonstrates that our program can handle the ZeroDivisionError gracefully without crashing.

1-exception-handling

Now let’s try an advanced example. Create a file called advanced_exception_handling.py, and type the following code:

def divide_with_multiple_exceptions(x, y):
    try:
        # Convert inputs to float to handle string inputs
        x = float(x)
        y = float(y)
        result = x / y
        print(f"The result is {result}")
    except ZeroDivisionError:
        print("You can't divide by zero!")
    except ValueError:
        print("Please enter valid numbers!")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")

# Test cases
print("Test 1: Normal division")
divide_with_multiple_exceptions(10, 2)

print("\nTest 2: Division by zero")
divide_with_multiple_exceptions(5, 0)

print("\nTest 3: Invalid input")
divide_with_multiple_exceptions("abc", 2)

print("\nTest 4: Float division")
divide_with_multiple_exceptions(10.5, 2.5)

This is an expanded version that shows how to handle multiple types of exceptions and includes more test cases. It:

  • Handles ZeroDivisionError
  • Handles ValueError for invalid inputs
  • Includes a general Exception catch for unexpected errors

In robotics, exception handling is essential for maintaining robust and reliable control. For instance, if a sensor fails or provides invalid data, handling these exceptions can allow the robot to continue operating or switch to a safe state rather than failing outright.

2-advanced-exception-handling

That’s it for this tutorial on handling exceptions. Thanks, and I’ll see you in the next tutorial.

Keep building!