Instance Methods That Make Python Objects Act

Resumen

Python instance methods turn plain data containers into objects that can act, decide, and respond. If you already know how to build a class, the next step is giving your objects real behavior, and that is exactly what instance methods do through the self parameter.

What are Python instance methods and why do they matter?

An instance method is a function defined inside a class that operates on each individual object. It uses self to read or modify the object's own attributes, which means every book, user, or product you create can carry its own state and react to actions.

What is self in Python? It is the reference to the current object. When you call my_book.lend(), Python automatically passes my_book as self, so the method knows which instance to work on.

In the example we are building, a Book class already has attributes like title, ISBN, and an available boolean. The books live inside a catalog list, and a for loop prints their data. That is fine, but it is static. Methods are what bring it to life [00:35].

How do you create an instance method that changes object state?

The first method we add is cambiar_disponibilidad. Every method in a class receives self by default, and inside the method you can validate the current state before changing it.

The logic is simple:

  • Check if self.available is True.
  • If it is, set self.available = False.
  • Call the method on the instance, for example my_book.cambiar_disponibilidad().

When you run python main.py, Cien años de soledad shows up as False while El Principito stays as True, even though both were created as available [01:45]. That single method already proves how an object can mutate its own data.

How does the __str__ method improve object printing?

Printing an object directly normally returns something unreadable. The dunder method __str__ fixes that by defining the written representation of the instance.

You declare it with def __str__(self): and return a formatted string using self.title, self.isbn, and the rest of the attributes. The key detail here is the return: if you forget it, Python throws an error because __str__ must always return a string [03:10].

Why use __str__ instead of a regular print? Because once defined, print(my_book) automatically calls it. You stop repeating format strings across your code and centralize how the object describes itself.

How do you design methods with clear business logic?

A method called cambiar_disponibilidad works for testing, but it does not describe what is really happening in a library. Splitting it into two methods makes the intent obvious and the code easier to maintain.

The refactor looks like this:

  1. Rename the toggle into prestar, which validates availability and sets self.available = False.
  2. Create devolver, which sets self.available = True.
  3. Return a message from each one so the caller knows what happened.

For prestar, the return is f"{self.title} prestado exitosamente". For devolver, the message is f"{self.title} ha sido devuelto y disponible nuevamente". Notice the f prefix: without it, the string is literal and the title never gets interpolated, which is exactly the bug shown when the message printed without the book name [05:20].

When should you use return versus print inside a method?

Returning a value is almost always the better practice. It lets the caller decide what to do, whether that is printing, logging, or chaining the result into another function.

  • Use return when the method produces information someone else might need.
  • Use print only for quick debugging or when the method's only purpose is displaying something.
  • Combine both during development: print(my_book.prestar()) lets you see the message while keeping the return value intact.

What happens if a method has no return? It returns None by default. That is why calling my_book.prestar() showed nothing on screen until the code was wrapped in a print().

What separates a smart object from a simple data container?

Instance methods are the line between both. A class without methods is just a structured dictionary. A class with methods can validate its own rules, change its internal state, and communicate results back to whoever called it.

The Book class now does three things on its own:

  • Knows how to describe itself through __str__.
  • Knows how to be lent through prestar, including the availability check.
  • Knows how to be returned through devolver, restoring its state.

Each method touches self to read or write attributes, which is the whole point of object oriented design in Python.

Your challenge: build an es_popular method

Add a new method called es_popular to the Book class. It should return True if the book has been lent more than five times.

To make this work, you will need to track every lending event, which means adding a list attribute that stores each time prestar is called. Think about where to initialize it and how to update it inside the existing methods.

Share your implementation in the comments and tell me how you decided to count the loans.