Let's talk in more details about methods, which were briefly mentioned earlier. In addition to the __init__ method we talked about before, we can also include any other methods we want for our classes. Methods are just another type of attribute, and you can think of them as functions that only objects of the same class can use. Let's look at an example with our book class (remember you still have to fill some of this out yourself in the file you downloaded):


class Book:
	def __init__(self, genre, title, author, publication_year):
		self.genre = genre
		self.title = title
		self.author = author
		self.publication_year = publication_year

	def calculate_age(self):
		return 2022 - self.publication_year
		

For our class, we have added a new method, called calculate_age. It returns the age of our book based on the current year (2022) and its year of publication.

To invoke our method, the most obvious way is as follows:


ranger_games = Book('Crime Nonfiction', 'Ranger Games', 'Ben Blum', 2017)
print(Book.calculate_age(ranger_games))
            

This code tells the Book class to run the calculate_age method on the instance of the Book class called ranger_games.

Try running the code on PythonTutor at this link.

Another way to do the exact same thing is to tell the instance itself to do the age calculation:


ranger_games = Book('Crime Nonfiction', 'Ranger Games', 'Ben Blum', 2017)
ranger_games.calculate_age()
						

This alternate syntax is EXACTLY identical in behavior.

Try running this alternate version of the code in Python Tutor at this link.

There are a few important things worth noting:

  1. Any method we write must have at minimum one parameter: self. If we use the class name to invoke the method, e.g. Book.calculate_age(ranger_games), then we must provide the book whose age is to be calculated. If we instead use the more common syntax where we invoke using the name of an instance, e.g. ranger_games.calculate_age(), then Python automatically fills in the first argument of the function with the instance whose method got called.
  2. The second style of method invocation (where self gets automatically filled in) is far more common. Don't use the first style (where you manually specify the argument for self) in real world code. The first style is so rare that many people don't even know that it's possible. In other words, the second style is more Pythonic.
  3. The name "self" is just a convention. You can technically put anything you want, e.g. def calculate_age(a_book), and it would just fine. However, other programmers might get confused that you're not using the usual convention of naming the first variable self.
  4. In the function body, it is essential to refer to any instance attributes with dot notation using self, otherwise our method will not be able to recognize what object we are referring to. For example, if our calculate_age method returned 2022 - publication_year, the code would just crash, since we don't know WHOSE publication year. You can try this out by editing the code on PythonTutor if you'd like.