Python Build a Social Network with Flask Making Strong Users Class Method

Welby Obeng
Welby Obeng
16,272 Points

@classmethod

Is @classmethod basically a way for you to call a method with initiating class?

What is the pros and cons in initiating it then calling method vs just calling method with @classmethod

6 Answers

Kenneth Love
STAFF
Kenneth Love
Treehouse Guest Teacher

Welby Obeng The main use of @[Akshay Nilangekar](http://teamtreehouse.com/cl0udkicker) is to provide an alternate constructor. Consider this example:

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

If I want to create a person I have to know the first name and the last name of that person (apologies to all of the people whose names don't match this description). That sucks if I have some other way of getting names. Maybe they always come in as a string with both name pieces in the string.

I can split them up and then create the people. Sure. But maybe the class creator was nice and gave me a way of doing all at once.

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def from_string(cls, name_string):
        names = name_string.split()
        return cls(first_name=names[0], last_name=' '.join(names[1:]))

Now I can do Person.from_string('Kenneth Love') and get back a Person object. I don't have to do that splitting work in my own business logic and everyone is more-or-less happy.

Hi Kenneth Love, I think your answer might have picked up the @ symbol and converted it to a Treehouse user mention... I assume you were trying to write @classmethod.

Kenneth Love
STAFF
Kenneth Love
Treehouse Guest Teacher

The basic reason to use @classmethod instead of just a regular method is whether you want to have an instance before you call the method.

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    def create(self, make=None, model=None, year=None):
        return Car(make=make, model=model, year=year)

Say I have the above class. If I want to use the Car.create() method, I have to have a car first.

>>> my_car = Car('subaru', 'forrester', 2005)
>>> another_car = my_car.create('chevy', 'focus', 2004)
>>> blank_car = Car.create('ford', 'taurus', 1998)
>>> blank_car.make
'taurus'
>>> blank_car.model
1998

That doesn't make a lot of sense, does it? Why would I create a car just so I can create a new car? And if I try to just use the method without an instance, all of the values are off by one. Let's make one small change.

class Car:
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    @classmethod
    def create(cls, make=None, model=None, year=None):
        return cls(make=make, model=model, year=year)

We'll add @classmethod to the method and changed self to cls. Due to how @classmethod works, cls now references the class (in this case, Car) instead of the instance, which is what self usually points to. I don't have to name it cls, of course, but it's a naming convention. What happens if I use it?

>>> my_car = Car.create('subaru', 'forrester', 2005)
>>> my_car.make
'subaru'
>>> my_car.model
'forrester'
>>> my_car.year
2005

Hey, it worked right! And I didn't have to create an instance first.

Welby Obeng
Welby Obeng
16,272 Points

In the first code example above, why not just create a new car by initiating a class? and not use the “create” method at all? Like

my_car1 = Car('subaru', 'forrester', 2005)
my_car2 = Car('chevy', 'focus', 2004)
etc

Also In the first code example how come you said :

return Car(make=make, model=model, year=year) 

and not

return self.Car(make=make, model=model, year=year)
Kenneth Love
Kenneth Love
Treehouse Guest Teacher

This example doesn't show it, but imagine you had a class that you always wanted created through a particular workflow. You wouldn't necessarily want it just created directly from the class. Or maybe it's just more convenient through that method.

For the second question, there's no such thing as self.Car. self is the instance, a Car object, and an instance doesn't have an attribute of it's class. You could do self.__class__ but that still requires an instance. The point of @classmethod is to not have an instance first.

Welby Obeng
Welby Obeng
16,272 Points

I'm still confused about the class method :(

I have read almost every article on google but it's still vague...

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

Honestly, most of the time, you won't need @classmethod or @staticmethod so don't worry too much about cementing them in your Python understanding just yet. You'll generally start to see where they're useful as you write more and more code.

That said, how can I help you understand it better? What parts, specifically, are still fuzzy?

Welby Obeng
Welby Obeng
16,272 Points

Can you give me one more real world example where I will typically do it the messy way and how it can be improved with class method with explanation pleaseeeeeee?

Thanks

I believe it's more about how the method is attached to the class. With the @classmethod decorator, all instances of the class (and of inherited classes) can use it, and it is run in the context of the class itself, not the instance of the class.

It's a particularly confusing topic. Here's a fairly in-depth article that attempts to explain it all: The definitive guide on how to use static, class or abstract methods in Python

Welby Obeng
Welby Obeng
16,272 Points

ahh I see, it's starting to make sense.

Does this mean that Person object that you just created will inherit from_string method? Like this

welby = Person.from_string('Welby Obeng') and I can use welby object to create another person like this kenneth = welby.Person.from_string('Kenneth Love') ?

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

welby doesn't have a Person attribute, so no.

Welby Obeng
Welby Obeng
16,272 Points

sorry I meant:

welby = Person.from_string('Welby Obeng') and I can use welby object to create another person like this

kenneth = welby.from_string('Kenneth Love') ?

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

That should work, yes. Since it's a @classmethod, it should know to peel the class off of the instance and use that to run the method.

Welby Obeng
Welby Obeng
16,272 Points

perfect...I get it now...

Welby Obeng
Welby Obeng
16,272 Points

This means that the blueprint to create an instance is given to the instance when you use @classmethod?

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

Well, the instance has a class and the class has this special constructor, so, yes. But instances always have access to their class and every class has an __init__ method, so they always have the ability to create a new instance if you go down the right path.

Y B
Y B
14,135 Points

I'm fairly comfortable with @classmethod and what it does, but I can't see it's use case, (other than possibly counting instances of the class that have been created).

In your car example here wouldn't you just create the class with the init constructer and amend that as needed. Using this we can create an instance without having to create another instance first.

Perhaps this can be done (and is normally) and as per your comment, @classmethod is only used in the construction of a class when we are restricted to a particular workflow.

In the example in the course could we just remove the create_user() method and replace it with an init()?

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

@classmethod, especially in this example, comes in handy when you need a different constructor. I want to be able to create User objects through their __init__, or normally, if needed. Maybe I need a fake User for something or maybe I just want to leave it alone just in case. Since I've created this @classmethod for creating users with a specific workflow, I can have both options all of the time.

Y B
Y B
14,135 Points

Thanks Kenneth that's helps to make it clearer