Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Object-Oriented Python Advanced Objects Constructicons

Why use @classmethod?

In the video Kenneth says that you do not want to pass a mutable argument as an argument when initializing the class. I don't get why?

class Book:

    def __init__(self, title, author):
        self.title = title
        self.author = author

    def __str__(self):
        return "{} by {}".format(self.title, self.author)


class BookCase:

    def __init__(self, books=None):
        self.books = books

    @classmethod
    def create_bookcase(cls, books):
        list_of_books = []

        for t, a in books:
            list_of_books.append(Book(t, a))

        return cls(list_of_books)

my_bookcase = BookCase([Book("Alice In Wonderland", "Lewis Carroll"),
                        Book("Sherlock Holmes", "Arthur Conan Doyle"),
                        Book("The Stranger", "Albert Camus")])

for i in my_bookcase.books:
    print(i)

my_bookcase_2 = BookCase.create_bookcase([("Alice In Wonderland", "Lewis Carroll"), 
                                          ("Sherlock Holmes", "Arthur Conan Doyle"), 
                                          ("The Stranger", "Albert Camus")])

for i in my_bookcase_2.books:
    print(i)

In the code above I achieve the exact same thing with my_bookcase as with my_bookcase_2 which uses the create_bookcase classmethod, so what is the point of using a classmethod?

And finally, why is books = None used instead of just books inside of the __init__()?

class BookCase:

    def __init__(self, books=None):
        self.books = books

4 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,457 Points

In this case, it is possible to do without the classmethod. The lesson is about the general use of classmethods and simple examples might seem contrived or trivially bypassed. In a more complicated example, the create_bookcase classmethod might provide more validation of the incoming books or add other attributes that might not be obvious outside of the Book class..

The books=None allows someone to create a bookcase instance without any supplied books. Since books is used in the __init__ code, it needs to have a default value or it will raise a NameError.

Post back if you have more questions!

Pete P
Pete P
7,613 Points

Based off of your answer, I'm wondering if this would be an example of where classmethods should be used.

Let's say I have a class that takes a file name as an argument. This file name could refer to a text file, excel file, or csv file. I need to perform different operations and set attributes differently based upon the type of file that is passed to the class.

So, rather than doing something like in this pseudo code:

class Thingy:
    def __init__(self, filename):
        if filename is txtfile:
            # validate file exists
            # read file
            # set attributes 
        elif filename is excel_file:
            # validate file exists
            # parse file / create pandas dataframe
            # get some data
            # set attributes
        else:
            # validate file exists
            # read in the csv
            # set attributes

I could (should?) define classmethods to handle each file type

class Thingy:

    @classmethod
    def data_from_txtfile(cls, file):
            # validate file exists
            # read file
            # set attributes
    @classmethod
    def data_from_excelfile(cls, file):
            # validate file exists
            # parse file / create pandas dataframe
            # get some data
            # set attributes
    @classmethod
    def data_from_csvfile(cls, file):
            # validate file exists
            # do stuff to the csv
            # set attributes

Or am I way off? Thanks for all of your help!

Chris Freeman
Chris Freeman
Treehouse Moderator 68,457 Points

Great follow-up question! It's helped me think more about how I would decide when to use a classmethod over an instance method. I came to this conclusion:

If a classmethod is intended to operate on the class and a regular instance method is intended to operate on an instance of the class, then a classmethod is best used to for functionality associated with the class, but that would not be useful if applied to an existing instance of a class. One example is helping construct specific classes instances.`

So in your example, I would have the __init__ method create the class from a data set and use the classmethods to prep the data set used by the __init__ method. Restructure it more like:

class Thingy:
    def __init__(self, data=None):
        # set attributes based on data

    @classmethod
    def data_from_txtfile(cls, file):
        # validate file exists
        # data = data read from file
        # return instance of class created from data
        return cls(data=data)

    @classmethod
    def data_from_excelfile(cls, file):
        # validate file exists
        # parse file / create pandas dataframe
        # data = some data from parse or dataframe
        # return instance of class created from data
        return cls(data=data)

    @classmethod
    def data_from_csvfile(cls, file):
        # validate file exists
        # do stuff to the csv
        # data = some data from csv file
        # return instance of class created from data
        return cls(data=data)

This StackOverflow question lists other examples on where classmethods are useful.

Hope this makes sence. As always, post back as needed!

Pete P
Pete P
7,613 Points

Makes sense. Completely cleared up my confusion. Thanks again!

Huston Petty
Huston Petty
2,833 Points

A better example, like the one above by Chris, needs to be used in the video. The one in the video is confusingly simple. I struggled for like 10 minutes to understand WHY you would want to create a class method that appeared to do exactly what __init__ would do. I kept asking myself "Why not just put all of that in the __init__?!". Maybe the video example just needs to show a few different class methods to show why they are useful. Like, a create_bookcase vs create_sorted_by_author_bookcase vs create_sorted_by_title_bookcase. Just a thought.

Aaron Banerjee
Aaron Banerjee
6,876 Points

Chris Freeman, so does the __init__ method interact with the class method to instantiate the data in the example above?

Chris Freeman
Chris Freeman
Treehouse Moderator 68,457 Points

The final statement in the class method is of the form

    return cls(data)

It is this call within the class method that instantiates the class and subsequently calls the __init__ method.

Chul Kim
Chul Kim
2,341 Points

Are class methods mostly used with parameters that are mutable?

I don’t know if I am understanding this correctly..it seems that there are two ways to control how instances are created.

The first, changing the __init__ method which controls how the parameters are applied and defined to the instance.

The second, you can use the same __init__ method except with the use of class methods you can decide which parameters get passed into the __init__ method which in turn you can create different instances with different parameters??

Chris Freeman
Chris Freeman
Treehouse Moderator 68,457 Points

Unsure what you mean by mutable parameters, since all parameters are mutable. Even parameters that are protected as properties through setters and getters maybe modified by both class methods and instance methods.

Many times a classmethod is used in the constructor process to run code before __init__ is run. But in general, a class method is used when you have a function that needs to executable before the instance is created.

This StackOverflow post talks through many uses for @classmethod.

One additional clarification: __init__ doesn’t create the instance, rather it modifies the instance after creation (which is why it dos not have a return statement). The __new__ method (typically inherented from object) creates the actual instance. __new__ calls __init__ and returns the new instance.