Look at it Now The Real Python team has also produced a video course that is complementary to this instruction. In order to further your comprehension, you should watch it in conjunction with the written tutorial: Python Object-Oriented Programming Method Types: @classmethod, @staticmethod, and Instance Methods
I’m going to help clear up some of the confusion around class methods, static methods, and ordinary instance methods by providing an in-depth explanation on the subject.
You will be able to build object-oriented Python code that expresses its goal more clearly and is simpler to maintain in the long term if you have an intuitive awareness of the distinctions between the two.
A General Explanation of Instance, Class, and Static Methods
To get started, let’s write a class in Python 3 that has some basic examples of each of the following three sorts of methods:
class MyClass:
def method(self):
return 'instance method called', self
@classmethod
def classmethod(cls):
return 'class method called', cls
@staticmethod
def staticmethod():
return 'static method called'
NOTE: For those that utilise Python 2: With Python 2.4, the @staticmethod and @classmethod decorators are available, and the code in this example is able to function without any changes. You may decide to define a new-style class inheriting from object using the syntax class MyClass(object): rather than using a simple class MyClass: declaration if you want to avoid using the superclass keyword. All else being equal, you should be fine to go.
The Techniques of Instances
The first method that can be found on MyClass is referred to simply as method, and it is a typical instance method. The most of the time, you will utilise a method type that is quite simple and does not provide any extra features. You can see that the method accepts a single argument, which is labelled self, and when the method is used, self will refer to an instance of MyClass (but of course instance methods can accept more than just one parameter).
Instance methods have unrestricted access to all of the properties and other methods that are associated with the same object thanks to the self argument. Because of this, they have a great deal of power when it comes to changing the condition of an item.
Instance methods not only have the ability to change the state of an object, but they also have access to the class itself through a property called self. class__. This indicates that instance methods have the ability to alter class state as well.
The Techniques of Class
Let’s evaluate this method in light of the second one, which is the MyClass.classmethod. In order to indicate that this method belongs to the class, I decorated it with the @classmethod marker.
Class methods do not accept a self argument as regular methods do; rather, they take a cls parameter that, when called, refers to the class itself rather than the object instance.
Due to the fact that the class method can only access the cls parameter, it is unable to affect the state of the object instance. In order to do that, you’d need access to yourself. Class methods, on the other hand, may still make changes to the state of the class, which affects all instances of the class.
Techniques That Do Not Move
The third method, referred to as MyClass.staticmethod, was designated as a static method by having the @staticmethod decorator applied to it.
This kind of method does not need a parameter named self, nor does it require a parameter named cls; nevertheless, it is free to accept any number of additional arguments.
As a result, a static method is unable to make changes to either the object state or the class state. Static methods have limitations placed on the kind of data to which they have access, and their primary use is to namespace other methods.
Let’s See Them In Action, Shall We?
I am aware that, up until this point, our conversation has been mostly theoretical. In addition, I feel it is essential for you to cultivate an intuitive grasp of the ways in which the various kinds of methods vary in their application. Now that we have some background, let’s look at some specific cases.
Let’s have a look at what happens when we call these methods and see how they behave in the real world. To begin, a new instance of the class will be created, and we will proceed to execute the three distinct methods on it.
Since MyClass was designed to work in a certain manner, the implementation of each method returns a tuple that tells us not just what portions of the class or object the method may access but also what is going on. This allows us to track what is happening.
When we invoke an instance method, the following events take place:
>>> obj = MyClass()
>>> obj.method()
('instance method called', <MyClass instance at 0x10205d190>)
This demonstrated that the method, also known as the instance method, has access to the object instance (which was written as ‘MyClass instance’) via the use of the self parameter.
When the method is invoked, Python will replace the self argument with the obj object that represents the instance. We might obtain the same result by ignoring the syntactic sugar of the dot-call syntax (obj.method()) and passing the instance object explicitly instead:
>>> MyClass.method(obj)
('instance method called', <MyClass instance at 0x10205d190>)
Do you have any idea what would take place if you attempted to invoke the method before you had first created an instance?
Instance methods may also access the class itself by using the self. class__ property. By the way, this is possible. Because of this, instance methods are quite powerful in terms of the access restrictions they may impose since they are able to affect the state of the object instance as well as the state of the class itself.
Let’s proceed to testing out the class method:
>>> obj.classmethod()
('class method called', <class MyClass at 0x101a2f4c8>)
The function classmethod() revealed to us that it does not have access to the ‘MyClass instance’ object; rather, it only has access to the ‘class MyClass’ object, which represents the class itself (everything in Python is an object, even classes themselves).
When we invoke the MyClass.classmethod function in Python, take note of how the class is immediately passed on to the function as the first parameter (). This behaviour is triggered whenever a Python method is called using the dot syntax. This also applies to the self parameter of instance methods.
Note that the name of these parameters as self and cls is only a tradition that has been followed here. The outcome would be the same whether you called them the object and the class or some other name entirely. It is unimportant where they appear in the parameter list for the method; all that counts is that they come first.
Now might be a good time to invoke the static method:
>>> obj.staticmethod()
'static method called'
Did you notice that when we used staticmethod() on the object, we were successful in doing what we wanted to do? When some developers discover that an object instance may be used to invoke a static method, they are taken aback by the revelation.
When a static method is called using the dot syntax, Python simply enforces the access restrictions by not handing in the self or the cls parameter. This occurs behind the scenes and is completely transparent to the user.
This demonstrates that static methods are unable to access either the state of the object instance or the state of the class. They behave similarly to conventional functions, except their place is in the namespace associated with the class (and each instance).
Now that we’ve seen what happens when we try to call these methods on the class itself – without first generating an object instance – let’s have a look at what happens when we don’t create an object instance first:
>>> MyClass.classmethod()
('class method called', <class MyClass at 0x101a2f4c8>)
>>> MyClass.staticmethod()
'static method called'
>>> MyClass.method()
TypeError: unbound method method() must
be called with MyClass instance as first
argument (got nothing instead)
We had no problems using classmethod() or staticmethod(), but when we tried to call the instance method method(), we got a TypeError instead.
This is something that should have been anticipated since this time around we did not attempt to generate a new object instance but rather sought to invoke an instance function directly on the class blueprint itself. This indicates that Python does not have any ways to fill the self argument, and as a result, the call is unsuccessful.
The differences between these three sorts of methods ought should be a bit easier to distinguish as a result of this. Yet, I am not going to stop there with this discussion. In the next two parts, I will provide two instances that are a little bit more practical for determining whether to apply these different kinds of special methods.
In order to illustrate my points, I will use the following pizza-themed class:
class Pizza:
def __init__(self, ingredients):
self.ingredients = ingredients
def __repr__(self):
return f'Pizza({self.ingredients!r})'
>>> Pizza(['cheese', 'tomatoes'])
Pizza(['cheese', 'tomatoes'])
Note: This code sample as well as the ones that come later in the course make use of Python 3.6 f-strings in order to generate the string that is produced by the __repr__ function. For Python 2 and versions of Python 3 prior to 3.6, you would make use of a different string formatting expression, such as the following:
def __repr__(self):
return 'Pizza(%r)' % self.ingredients
Wonderful Pizza Manufacturing Facilities Using @classmethod
If you’ve ever eaten pizza in the real world, you’re probably aware that there are numerous tasty topping combinations to choose from, such as the following:
Pizza(['mozzarella', 'tomatoes'])
Pizza(['mozzarella', 'tomatoes', 'ham', 'mushrooms'])
Pizza(['mozzarella'] * 4)
The Italians worked out their pizza taxonomy centuries ago, and as a result, each of these delectable varieties of pizza has its own specific name. It would be to our benefit to take use of this and provide users of our Pizza class with an improved interface for the creation of pizza objects that satisfy their cravings.
Using class methods as factory functions for the many types of pizzas that we are able to produce is a strategy that is both aesthetically pleasing and functional.
class Pizza:
def __init__(self, ingredients):
self.ingredients = ingredients
def __repr__(self):
return f'Pizza({self.ingredients!r})'
@classmethod
def margherita(cls): return cls([‘mozzarella’, ‘tomatoes’])
@classmethod def prosciutto(cls): return cls([‘mozzarella’, ‘tomatoes’, ‘ham’])
Take note of how I’m contacting the Pizza function Object() { [native code] } indirectly by utilising the cls parameter in the margherita and prosciutto factory methods instead of directly using the Pizza function Object() { [native code] }.
If you want to adhere to the Don’t Repeat Yourself (DRY) concept, you may utilise this strategy as a trick. In the event that we change our minds and decide to rename this class at some point in the future, we won’t have to worry about remembering to update the function Object() { [native code] } name in each of the classmethod factory methods.
So, what exactly are some of the possibilities with these manufacturing processes? Let’s put them to the test:
>>> obj = MyClass()
>>> obj.method()
('instance method called', <MyClass instance at 0x10205d190>)
0
As can be seen, we are able to utilise the factory functions to generate brand-new Pizza objects that are set up in the manner that best suits our needs. They all make use of the same __init__ function Object() { [native code] } on the inside, and all they do is provide a convenient shortcut for remembering all of the different components.
You are able to specify different constructors for your classes if you make use of class methods, which is another way of looking at this use of class methods.
Python classes are limited to having a single __init__ method at their disposal. It is feasible to add any number of alternative constructors that are required by making use of class methods. This can make the interface for your classes self-documenting, at least to some degree, and can also make using them easier.
When It Is Appropriate To Utilize Static Methods
Coming up with a suitable example to illustrate this point is a little bit more challenging. But I’ll tell you what, I’m simply going to keep trying to make the comparison of pizza thinner and thinner… (yum!)
The following is what I have come up with:
>>> obj = MyClass()
>>> obj.method()
('instance method called', <MyClass instance at 0x10205d190>)
1
What exactly did I alter in this sentence? First, I added support for an additional radius parameter to the function Object() { [native code] } as well as the __repr__ function.
In addition to that, I included an instance method called area() that computes and returns the pizza’s area (this would also be a good candidate for a @property, but this is just a toy example).
I factored it out to a new circle area() static method rather than calculating the area directly inside the area() function, making use of the well-known formula for computing the area of a circle.
Why don’t we give it a shot?
>>> obj = MyClass()
>>> obj.method()
('instance method called', <MyClass instance at 0x10205d190>)
2
Even though this is something of an oversimplified example, it will serve its purpose in assisting with the explanation of some of the advantages that static approaches provide.
Since they do not need a cls or self argument, static methods are unable to access the state of a class or an instance. This is something that we have discovered. It is a significant restriction; but, it is also an excellent indicator that a given approach is independent from everything else that is occurring in its immediate environment.
Based on the above example, it is quite evident that the circle area() function is unable to make any kind of change to either the class itself or an instance of the class. (It’s true that you could get around that by using a global variable, but that’s not the point.)
So, why exactly is that helpful?
This limitation is not only enforced by the Python runtime, but it is also indicated by the fact that marking a method as a static method indicates that it will not affect the state of the class or instance.
Methods such as those enable you to communicate clearly about components of your class architecture, which ensures that newly developed work is naturally steered to occur inside the bounds that you have established. There is no question that flouting these rules would not be difficult in the least. In contrast, however, they are useful in practise since they often assist in preventing unintentional adjustments that go against the original concept.
To put it another way, the use of static methods and class methods are techniques to express the goal of the developer while also enforcing that meaning enough to prevent the majority of slips of the mind and faults that would destroy the design.
Writing some of your methods in that manner may bring advantages for maintenance and reduce the likelihood that other developers would use your classes in an inappropriate manner, but it should be done so sparingly and only when it makes sense to do so.
When it comes to building test code, static methods provide a number of advantages as well.
It is a lot simpler to test the circle area() function than any other method in the class since it is totally separate from the other methods.
While doing a method test using a unit test, it is not necessary for us to worry about creating an instance of the class in its entirety beforehand. We are free to continue firing away as if we were testing a standard function. One more time, this will make future maintenance much simpler.
Important Takeaways
- Instance methods need a class instance and can access the instance through
self
. - Class methods don’t need a class instance. They can’t access the instance (
self
) but they have access to the class itself viacls
. - Static methods don’t have access to
cls
orself
. They work like regular functions but belong to the class’s namespace. - Static and class methods communicate and (to a certain degree) enforce developer intent about class design. This can have maintenance benefits.
Mark this item as finished.
Look at it Now The Real Python team has also produced a video course that is complementary to this instruction. In order to further your comprehension, you should watch it in conjunction with the written tutorial: Python Object-Oriented Programming Method Types: @classmethod, @staticmethod, and Instance Methods