Skip to main content

Getting started with classes in Python Part 1: Anatomy of a Class

james.derrick@ansys.com | 10.18.2024

In Python you may hear this phrase said a lot "everything is an object", which they are! Objects, that is. But what this means is that everything is really (if you look 'under the hood') either a class, or an instance of a class ("but what does that mean?!", don't worry, we'll get there). Once you have learnt how to make classes, and manipulate them, you gain the ability to create your own objects as well as edit existing ones as you may like. Now, this is a lot and naturally with great power comes great responsibility and it would be remiss for me to explain how you can break everything without explaining how to fix it as well.

However, ultimately, all this wild potential is well beyond even intermediate level and when you're just starting out you really only need the basics of classes and how they can be useful. Normally there's a lot of difficult syntax you need to learn, but in Python 3.7 they introduced dataclasses which is a simple package that makes simple class creation and use easy.

Anatomy of a class in Python

Before I can explain how to use dataclasses in meaningful detail, however, we need to go over what the various bits of a class are. Part 1 of this article is a quick primer of what a class looks like and what the various bits of it are called. I am using the built-in Python library dataclasses to demonstrate them here which is actually a simplified form of regular classes, but that's the point. These are a lot easier than normal classes and I will get to what the syntax means and how to make your own in Part 2. For now, Part 1 is a quick guide to what we call all the bits and pieces that make up a typical class and its usage.

  • Classes are defined like functions, except you use the keyword class instead of def followed by the class name and then a colon.
    • Class names should always be in UpperCamelCase format, with the first letter of each word capitalised and with no spaces.
from dataclasses import dataclass


@dataclass
class HatchBack:
    make: str
    model: str
    weight: float

    def get_summary(self):
        return f'This Hatch back is a {self.make} {self.model} and it weighs {self.weight} kg'


car = HatchBack('Skoda', 'Fabia', 1200.)
summary = car.get_summary()
print(summary)
print(car.make)
  • The code following the class name that is indented explains what the class is and how it behaves, like a function
    • the whole thing is known as the definition
  • Any objects created from the class are known as instances of that class.
    • in the code snippet above the variable car contains an instance of HatchBack.
  • If a class has any functions defined in the class definition, these are often referred to as being "on" the class.
  • Functions on a class are known as methods.
    • get_summary is a method on HatchBack.
    • "methods" are called using Python's dot notation, for example car.get_summary() in the preceding example.
    • Class methods should always* include the parameter self which refers to the object itself, get it?
  • You can access, and modify, the variables "on" a class. These are known as "properties".
    • For example, in the preceding example, the class properties are make, model, and weight.
    • Properties are accessed using dot notation.
  • This isn't strictly true as there are a method types that require other parameters instead of self, like classmethods and staticmethods, but those are for another time.
  • More on self.
    • You do not need to provide an argument corresponding to self** when calling a method, as shown in the preceding example.
    • Calling self "self" is actually a convention thing and you could technically use any name you like (so long as it's consistent within your class).
    • However, everyone uses self so you may just introduce a lot of unnecessary confusion if you decided to.

** If you've ever called a method on an object in the past with an argument when you shouldn't have you may have seen TypeError: HatchBack.get_summary() takes 1 positional argument but 2 were given, or a variant, before. This is why! self counts as a parameter and the argument is automatically supplied by Python at runtime, so if you try to add something in it complains of getting two arguments because Python already gave the method one when we weren't looking.

Decorators and the @ symbol

At this point I suspect you, as the reader, have a simple question in your mind.

"Hang on, are you just going to skip over what that @ means in the example?"

Aha! You got me. Well done for noticing; that actually is an advanced form of Python syntax indicating the application of what's known as a "decorator". Decorators in Python are special functions that can be applied directly to existing functions that change their behaviour at runtime without needing to edit the code. They are functions that accept functions as arguments and return functions as output. They are an example of meta-programming.

What that word salad means is that decorators modify function and class definitions but preserve their names (unless the decorator explicitly affects the name), so you can still call them the same way. For example, you could write a simple decorator called print_my_args which prints the arguments supplied to any function that the decorator has been applied to. Its application would look like this.

@print_my_args
def func(a, b, c):
    return a*b + c

I hope to cover decorators in depth in a future article, but for now you should know that you only need to be able to recognise them and how to apply them at this level. Creating your own is advanced Python that is well beyond the level in this article series.

Other Reading

This is the first in a ten-part series on classes in python. For a complete look at the articles in this series, have a look at this overview page: Getting started with classes in Python