Mojo vs Python: Comparison with Examples - Part IV: Struct vs Class

enter image description here

This article is part of a series dedicated to comparing Mojo and Python. After the speed comparison in the previous article, we now turn to the comparison of Mojo structs with Python classes.

Mojo Update

In November 2024 Magic was released, a new virtual environment and package manager that replaces the Modular CLI. Mojo’s latest version, 24.5.0, is available through the new Magic package manager. Install Magic and set up your repository according to the guidelines here. If you have an existing repository, like me, just run magic init inside the repository, update the Mojo version if required, and you can run your Mojo file with magic run mojo your-mojo.mojo. You can read about the changes brought by Mojo 24.5.0 here.

Python Class vs Mojo Struct

Mojo currently doesn’t support classes like Python, though future updates aim to include them to align with Python’s behavior. Instead, in Mojo we use structs. As the documentation states, a Mojo struct is a data structure that allows you to encapsulate fields and methods that operate on an abstraction, such as a data type or an object. Fields are variables that hold data relevant to the struct, and methods are functions inside a struct that generally act upon the field data. So far, sounds pretty much like a class. However. Python classes are dynamic, meaning you can add or delete attributes/methods of an object at runtime. Mojo’s structs are static, bound at compile-time, so you cannot change it dynamically at runtime. However, Mojo’s structs are faster and memory safe, as they are statically typed and compiled ahead of time. Another difference is that while Python classes may have class attributes, Mojo structs don’t support it. Also, Mojo structs don’t support inheritance either.

Let’s compare a Python class and a Mojo struct side by side.

The Example

You can find the complete code in my GitHub repository dedicated to comparing Mojo with Python.

I created a class/struct that represents a car. Its attributes are brand, model, year and color. Apart from the constructor, __init__, and the __str__ magic method, I added two instance methods, get_year that returns the year, and update_color that changes the car’s color as per the argument. At last, I implemented a static method that creates a Car object from a dictionary.

Python:

class Car:

    def __init__(self, brand: str, model: str, year: int, color: str):
        self.brand: str = brand
        self.model: str = model
        self.year: int = year
        self.color: str = color

    def __str__(self) -> str:
        return '{} | {} | {} | {}'.format(self.brand, self.model, 
                                          self.year, self.color)

    def get_year(self) -> int:
        return self.year

    def update_color(self, color: str):
        self.color = color

    @staticmethod
    def create_from_dict(car_dict: dict) -> Car:
        return Car(**car_dict)

Examples of working with Car objects:

# Honda Civic
car_1 = Car('Honda', 'Civic', 2000, 'green')
print(car_1)
car_1.update_color('grey')
print(car_1)

# Toyota Corolla
toyota_corolla = {'brand': 'Toyota',
                  'model': 'Corolla',
                  'year': 2018,
                  'color': 'black'}
car2 = Car.create_from_dict(toyota_corolla)
print(car2)
print(car2.get_year())

Output:

Honda | Civic | 2000 | green
Honda | Civic | 2000 | grey
Toyota | Corolla | 2018 | black
2018

Now, let’s see the same example using a Mojo struct.

Mojo:

struct Car:

    var brand: String
    var model: String
    var year: Int16
    var color: String

    fn __init__(inout self, brand: String, model: String, year: Int16, 
                color: String):
        self.brand = brand
        self.model = model
        self.year = year
        self.color = color

    fn __str__(self) -> String:
        return self.brand + ' | '  + self.model + ' | '  + 
               str(self.year) + ' | '  + self.color

    fn get_year(self) -> Int16:
        return self.year
    
    fn update_color(inout self, color: String):
        self.color = color

    @staticmethod
    def create_from_dict(car_dict: Dict[String, String]) -> Car:
        return Car(car_dict['brand'], car_dict['model'], 
                   int(car_dict['year']), car_dict['color'])

Examples of working with Car objects:

# Honda Civic
var car1: Car = Car('Honda', 'Civic', 2000, 'green')
print(str(car1))
car1.update_color('grey')
print(str(car1))

# Toyota Corolla
var toyota_corolla: Dict[String, String] = Dict[String, String]()
toyota_corolla['brand'] = 'Toyota'
toyota_corolla['model'] = 'Corolla'
toyota_corolla['year'] = '2018'
toyota_corolla['color'] = 'black'
var car2: Car = Car.create_from_dict(toyota_corolla)
print(str(car2))
print(car2.get_year())

Output:

Honda | Civic | 2000 | green
Honda | Civic | 2000 | grey
Toyota | Corolla | 2018 | black
2018

As I mentioned in previous articles, in Mojo a def function provides more dynamic features, while fn is stricter but safer and faster. When it comes to structs, we can still choose to declare methods with def or fn, but all of the struct fields must be declared with var.
Just like in Python, instance methods have self as their first argument. It refers to the object itself. You can call it anyhow, but self is the convention. In the constructor and the update_color method I marked self as inout which declares it as a mutable reference.
Mojo’s special methods basically match those in Python, giving it a Pythonic style. In this example I implemented __str__ which is called by str() and it returns the informal string representation of the object.
The staticmethod annotation is used in Mojo just like in Python, reminding us again of Mojo’s Pythonic style.

In summary, while Mojo structs share great similarities with Python classes, there are significant differences that set them apart.

This article ends here, I’ll be thinking on the next one soon by the Christmas tree. As the year is ending, try to have some rest and enjoy the holidays.
Good luck on your journey with Mojo and Happy Coding! 🔥🔥🔥

Comments