A guide to class types in Python

An explainer of what class types are, why they’re important, and how to make your own custom ones in Python, by Pluralsight author Maaike van Putten

Dec 15, 2025 • 12 Minute Read

Please set an alt value for this image...
  • Software Development
  • Guides
  • Python
  • Beginner

When we start learning Python, one of the first things we encounter are the built-in types. For example, we use:

  • Strings for text
  • Integers for whole numbers
  • Floats for decimals
  • Lists for collections
  • Dictionaries for key-value pairs

...and so on. But what you might not realise right away is that all of these are actually class types. For example, when you create a string like name = "Ali", you're creating an instance of the str class. And when you create a list like books = [], you're creating an instance of the list class. These built-in types are incredibly useful and cover a lot of what we need for basic programming.

But what happens when none of these types fit what we're trying to build? What if we need something more specific to our problem?

This happens more often than you’d think. Let’s say we’re trying to represent a book with a title, authors, ISBN, and availability status. A dictionary could work, but it's not ideal. You could make a lot of different mistakes, like:

  • Accidentally misspell a key
  • Forget to include important information
  • Store the wrong type of data in one of these fields

All of these issues could cause errors, bugs, and other issues (obviously not ideal.) We’d want some sort of blueprint that specifies what a book data type should be, and makes sure none of those things can happen. That’s where creating our own classes comes in.

Just like Python gives us str, int, and list, we can create our own Book, Patron, and Library types. These custom types can have exactly the attributes and behaviors we need.

The problem with handling data without classes

A long, long time ago, there were no programming languages with classes. Looping back to our book example, everything had to be handled like this:

      # Book 1
book1_title = "Python Illustrated"
book1_authors = "Maaike van Putten and Imke van Putten"
book1_isbn = "978-1836646334"
book1_available = True
book1_year = 2025

# Book 2
book2_title = "Learn Java with Projects"
book2_authors = "Dr. Seán Kennedy and Maaike van Putten"
book2_isbn = "978-1837637188"
book2_available = False
book2_year = 2023

# Book 3
book3_title = "JavaScript from Beginner to Professional"
book3_authors = "Laurence Svekis and Maaike van Putten"
book3_isbn = "978-1800562523"
book3_available = True
book3_year = 2021

def display_book(title, authors, isbn, available, year):
    print(f"Title: {title}")
    print(f"Authors: {authors}")
    print(f"ISBN: {isbn}")
    print(f"Available: {'Yes' if available else 'No'}")
    print(f"Published: {year}")

# Display books and please notice how error-prone this is
display_book(book1_title, book1_authors, book1_isbn, book1_available, book1_year)
display_book(book2_title, book2_authors, book2_isbn, book2_available, book2_year)
display_book(book3_authors, book3_title, book3_isbn, book3_available, book3_year) 

    

I just got a headache looking at this code, because the above is a list of problems just waiting to happen. Here’s why.

1. The code is incredibly repetitive

Typing out the code above would be a tedious task. Imagine tracking 1,000 books that way. You’d need need 5,000 variables!

2. There’s no context-specific error handling

Did you see something wrong with the last function call in the code? I “accidentally” swapped the title and authors for book 3. Python has absolutely no way to catch this mistake, and chances are you missed it too. Even if we run it, the code runs just fine, but the output is wrong.

Why? There’s no relationship between data. Python doesn't know that book1_title, book1_authors, and book1_isbn are all related to the same book. They're just random variables floating around in the Python memory now.

3. It’s hard to maintain and modify

What if we need to add genre information? We'd have to create a new variable for every single book and update every function call wherever we are using books.

That's not fun. At all.

Even when you try and use dictionaries, it’s not a good fit

Below is an example of another non-class approach we might take, this time using dictionaries instead:

      book1 = {
    "title": "Python Illustrated",
    "authors": "Maaike van Putten and Imke van Putten",
    "isbn": "978-1836646334",
    "available": True,
    "year": 2024
}

book2 = {
    "title": "Learn Java with Projects",
    "authors": "Dr. Seán Kennedy and Maaike van Putten",
    "isbn": "978-1837637188",
    "available": False,
    "year": 2023
}

def display_book(book):
    print(f"Title: {book['title']}")
    print(f"Authors: {book['authors']}")
    print(f"ISBN: {book['isbn']}")
    print(f"Available: {'Yes' if book['available'] else 'No'}")
    print(f"Published: {book['year']}")

display_book(book1)

    

This is definitely better, but it still has issues.

One of the main ones is that we have silent failures for types. If I accidentally write book['titel'] (“titel” is Dutch for “title” so that could easily happen) instead of book['title'], I'll get a cryptic error at runtime. The code editor won't warn you while we're writing this.

And more things can go wrong easily, because there's no clear structure. The logic for working with books is scattered across functions. There's no obvious connection between a book and what you can do with it.

This is exactly what classes solve. And honestly, once you get used to them, you'll wonder how you ever lived without them.

How to create a class in Python

A class is a blueprint for creating your own custom data type. Python has built-in types like str, int, and list, and we can create a Book type that fits exactly what we need.

Here's the simplest possible class:

      class Book:
    pass

    

The class keyword starts the definition, followed by the class name. By convention, class names use PascalCase and not snake_case. The pass keyword is just a placeholder that says "this class doesn't do anything yet".

When we have a class, we can use it to create instances (also called objects) of this class like this:

      my_book = Book()
another_book = Book()

    

Each instance is a separate book with its own identity. Think of the class as a cookie cutter and instances as the individual cookies. Same shape, but each one is distinct and can have different decorations.

Adding attributes to your class

Our class is a bit too boring right now. Let's add some attributes. Attributes are pieces of data that belong to each instance of your class. You can add them like this:

      class Book:
    pass

illustrated = Book()
illustrated.title = "Python Illustrated"
illustrated.authors = "Maaike van Putten and Imke van Putten"
illustrated.isbn = "978-1836646334"
illustrated.available = True

print(f"{illustrated.title} by {illustrated.authors}")

    

This works, but it's not great. Every time we create a Book, we have to manually set all its attributes. If we forget one, we'll get an error later when we try to use it. This is where constructors come in.

Creating a constructor (init) in Python

A constructor is a special method that runs automatically when we create a new instance of a class. In Python, the constructor is called __init__ (that's two underscores, the word "init", and two more underscores). Let's add one:

      class Book:
    def __init__(self, title, authors, isbn):
        self.title = title
        self.authors = authors
        self.isbn = isbn
        self.available = True  # All new books start as available

    

When we create a book, we pass in the values. And it's a lot cleaner to create a book, we can do it like this:

      illustrated = Book("Python Illustrated", "Maaike van Putten and Imke van Putten", "978-1836646334")
java_book = Book("Learn Java with Projects", "Dr. Seán Kennedy and Maaike van Putten", "978-1837637188")

print(f"{illustrated.title} by {illustrated.authors}")
print(f"{java_book.title} by {java_book.authors}")

    

Let's break down what's happening in the constructor:

The self parameter

The self parameter represents the specific instance being created. When you write illustrated = Book(...), inside the __init__ method, self refers to illustrated. It's how the method knows which book it's working with. Every method in a class needs self as its first parameter.

The attributes in the body

In the body of the constructor we’ve set some attributes, such as self.title = title. This takes the title parameter and stores it as an attribute of this specific book. The self.title part creates the attribute, the title part is the parameter value that we passed in.

Default values

On the last line of the body, we set available = True for all new books. This makes sense for a library system, books start on the shelf, available for checkout.

Expanding our constructor

Let's expand our constructor to include more information:

      class Book:
    def __init__(self, title, authors, isbn, year, genre):
        self.title = title
        self.authors = authors
        self.isbn = isbn
        self.year = year
        self.genre = genre
        self.available = True
        self.checked_out_by = None  # No one has it yet
        self.due_date = None

# Create books with all their information
illustrated = Book("Python Illustrated", "Maaike van Putten and Imke van Putten", "978-1836646334", 2024, "Programming")
java_book = Book("Learn Java with Projects", "Dr. Seán Kennedy and Maaike van Putten", "978-1837637188", 2023, "Programming")

print(f"{illustrated.title} ({illustrated.year}) - {illustrated.genre}")

    

Now every Book instance is guaranteed to have all these attributes initialized. We can't forget to set the title or accidentally set the ISBN to something weird. The constructor enforces the structure.

Adding methods to a class in Python

Methods define what objects of that class can do. They are functions that belong to a class. For our book class we'll have to think about actions that need to exist on a specific book. Well, books need to be checked out, returned, and displayed.

Let's add these capabilities to the class, like this:

      class Book:
    def __init__(self, title, authors, isbn, year, genre):
        self.title = title
        self.authors = authors
        self.isbn = isbn
        self.year = year
        self.genre = genre
        self.available = True
        self.checked_out_by = None
        self.due_date = None
    
    def display_info(self):
        print("=" * 40)
        print(f"Title: {self.title}")
        print(f"Authors: {self.authors}")
        print(f"ISBN: {self.isbn}")
        print(f"Published: {self.year}")
        print(f"Genre: {self.genre}")
        print(f"Available: {'Yes' if self.available else 'No'}")
        if not self.available:
            print(f"Checked out by: {self.checked_out_by}")
            print(f"Due date: {self.due_date}")
        print("=" * 40)
    
    def check_out(self, patron_name, due_date):
        if not self.available:
            print(f"Sorry, {self.title} is already checked out.")
            return False
        
        self.available = False
        self.checked_out_by = patron_name
        self.due_date = due_date
        print(f"{self.title} checked out to {patron_name}")
        print(f"Due date: {due_date}")
        return True
    
    def return_book(self):
        if self.available:
            print(f"{self.title} was not checked out.")
            return False
        
        print(f"{self.title} returned by {self.checked_out_by}")
        self.available = True
        self.checked_out_by = None
        self.due_date = None
        return True

# Use the class
illustrated = Book("Python Illustrated", "Maaike van Putten and Imke van Putten", "978-1836646334", 2024, "Programming")

illustrated.display_info()
illustrated.check_out("Mustafa", "November 21, 2025")
illustrated.display_info()
illustrated.return_book()
illustrated.display_info()

    

You can see that we create them like we'd create a function, but then inside the class and they always start with the self parameter. Every method takes self as its first parameter, representing the specific instance the method is called on. When you write illustrated.check_out("Imke", "Dec 1"), Python automatically passes illustrated as self.

Inside methods, we use self.attribute_name to access or modify the instance's data. When check_out sets self.available = False, it's changing that specific book's availability, not the availability of any other book.

When we call methods, we use the dot notation: object.method(). The object before the dot becomes self inside the method.

Just like functions, methods can return values just like functions. The check_out method returns True if successful and False if the book was already checked out.

The cool thing about this approach is that each book knows how to manage itself. You don't need separate functions that take a book as a parameter for these actions. The functionality is built right into the Book class.

How to use classes in Python

When we want to work with classes, we always have to start with creating an instance. We use the class name like a function to call the constructor and pass the parameters we need to the constructor, like this

      my_book = Book("Python Illustrated", "Maaike van Putten and Imke van Putten", "978-1836646334", 2024, "Programming")
    

The constructor then returns the book object and in this case it's stored in my_book. We have access to the attributes with the dot notations. This way we can read the parameters and modify them as well, like this:

      print(my_book.title)  # Read
my_book.available = False  # Modify

    

The dot notation is also used for calling methods, we always specify the parentheses and just provide any needed arguments. Here's two examples:

      my_book.display_info()
my_book.check_out("Tesfahun", datetime.now() + timedelta(days=14))

    

We can use attributes and methods in conditions like any value, here are two examples of that:

      if my_book.available:
    print("This book can be checked out")

if my_book.is_overdue():
    print("This book is overdue!")

    

Classes are great for organizing your code. And that's why there are a lot of them built-in already. Let's talk about those.

Python’s built-in class types, explained

Python comes with many built-in classes that we're using all the time when we work with Python. It's easy to miss that you are working with a class, because the logic is hidden. I do want to emphasis though, because understanding how we are working with the built-in classes helps you understand how classes work.

1. Strings

Strings are objects of the str class. We don't need to call the constructor here, we can just use the quotes and Python will create the string instance for us.

      book_title = "Python Illustrated"
    

A string is a class with method, so on our string instance we can call methods, like this:

      print(book_title.upper())  # Calling the upper() method
print(book_title.replace("Python", "Java"))  # Another method, replace()

    

2. Lists

Let's look at another built-in type, lists! Lists are objects of the list class. And again, we don't need to call the constructor, but we can indicate that we want them with special characters like this:

      books = ["Python Illustrated", "Learn Java with Projects", "JavaScript from Beginner to Professional"]
    

And then we can use the lists methods, such as:

      books.append("Java memory management")  # Method to add an item
books.sort()  # Method to sort the list A-Z

    

4. Other built-in types

There are a lot of built-in types, even numbers are objects! In fact, every value is an object in Python.

      number = 42
print(number.bit_length())  # Method that returns bits needed

    

So when we write "hello".upper(), we're calling the upper() method on a str object, just like when we write my_book.display_info(), you're calling the display_info() method on a Book object. The principle is exactly the same.

How to check what a class is with Python’s type function

If you’re unsure what class (or type) something is, then you can find out with the type function. Here’s an example:

      illustrated = Book("Python Illustrated", "Maaike van Putten and Imke van Putten", "978-1836646334", 2024, "Programming")

print(type(illustrated))  # <class '__main__.Book'>
print(type("hello"))  # <class 'str'>
print(type(42))  # <class 'int'>
print(type([1, 2, 3]))  # <class 'list'>

print(isinstance(illustrated, Book))  # True
print(isinstance("hello", str))  # True

    

Conclusion: Classes greatly improve your Python code

If we compare our original approach (even the dictionary one) to our class-based solution, it’s easy to see the advantages:

  • It’s a lot more readable
  • There’s error-handling to prevent mistakes
  • It’s a lot easier to maintain

Creating classes is something that we'll be doing often when we create programs with Python. And it's great, because it lets us create custom types that perfectly fit the problem.

In this article, we used it for creating a solution for libraries, but classes are far more broadly useful. Classes makes programming feel like describing the world we're trying to build. Whether it's a library system, a game, a web application, or anything else, classes help to organize complexity and create code that's easier to understand, test, and maintain.

Yes, classes definitely take a bit of getting used to. But once you get the hang of them, you'll wonder how you ever lived without them.


Want to start learning Python? Check out Pluralsight's Python 3 Learning path, which covers not only the fundamentals of python, but also advanced concepts such as object-orientated design and effective code organization. It includes hands-on learning labs and skill tests to measure your progress. Additionally, you can check out other Python tutorials on the Pluralsight blog by Maaike van Putten:

Maaike van Putten

Maaike v.

Maaike is a trainer and software developer. She founded training agency Brightboost in 2014 and spends most of her days and nights working and learning. Training gives her the opportunity to combine her love for software development with her passion to help others boost their careers and be successful. She has trained professionals in the field of Java, Spring, C#, Python, Scrum, React and Angular. A lot of her time is spend staying up-to-date with the latest developments in her field.

More about this author