You must own this product to watch this lesson.

Locked learning resources

You must own this product to watch this lesson.

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please refer to our video player troubleshooting guide for assistance.

OOP Method Types Comparison

00:00 Hey there! It’s Dan. Today I want to talk about the difference between class methods, static methods, and plain, or regular, instance methods in Python. Kind of demystify some of differences between them, when you would use each, what are some of the common scenarios, and how you can use them to actually make your code cleaner and more maintainable and easier to work with in the future.

00:24 Okay, so let’s jump right in. I want to make this really hands-on and work from a simple example that I can use to show you the difference. So, I created this class, I call it MyClass. It’s just a really simple example, and it’s got a couple of different method types on it.

00:42 So, the first one is just called method(), or the method is called method(), and it’s just a plain instance method. And you can see here, it takes one argument, the self argument, which points to the object instance.

00:57 That means within .method(), we can actually modify or read attributes on the object instance. And when you compare that to a class method—so, I just called this one classmethod() and then use the @classmethod decorator to actually mark it as a class method—you can see here that the .classmethod() only has access to this cls (class) parameter,

01:25 or cls argument. It doesn’t have have a self argument. So, this means a class method can only access the class itself, or the object representing the class, because well, everything is an object in Python. But the key difference is that when I create an instance of MyClass and call .classmethod() on it, it won’t be able to actually access the self object, right? So, it can only access attributes that actually exists on the class itself, and not on the instance. Now, with a static method, again, the approach is really similar.

02:03 You just define a regular method and then mark it with the @staticmethod decorator. What you can see here is that it doesn’t take any arguments at all, so it has no access to the class or the object instance at all.

02:19 It’s completely separate from that, and it’s really just a way to namespace your methods. So now, you know, I know this is very theoretical at this point, and it’s going to become much more clear when we actually try and do some experimentation with this stuff—some hands-on work.

02:39 So, the first thing I want to show you here is that when I create an object instance based on this class, I can actually call any of these method types on that object.

02:53 So, I can call a plain method—and I kind of structured these methods in a way that they return a string that kind of explains what’s going on. All right, so when I call obj.method(), we can see here that, well, we called the instance method and we had this self object pointing to an instance of MyClass.

03:16 Now, I can do the same thing with a class method, and now when I call this, you can see here that, again, we’re calling this .classmethod() method, and this time we also have access to this class object.

03:33 Now, the difference is that with the instance method, we actually had access to the object instance, right? So, this was an instance of a MyClass object.

03:44 Whereas with the class method, we have access to the class itself, but not to any of the objects because the class method doesn’t really care about an object existing. However, you can both call .classmethod() and .staticmethod() on an object instance.

04:06 This is going to work—it doesn’t really make a difference. So again, when you call .staticmethod() here, it’s going to work and it’s going to know which method you want to call, but really, the key difference now is going to be that we can also say, okay, MyClass—and I’m not creating an instance of this class—and I can go in and say .classmethod(), for example.

04:29 This is going to work fine. And I can also go ahead and call the .staticmethod(). This is also going to work fine. But of course, when I try and call .method(), that’s going to fail because we didn’t actually call it with a class instance.

04:48 So, I hope this makes this distinction between regular methods and static and class methods a little bit more clear. Now, of course, the big question is, “Okay, why…why do we need that? Why… why is that a thing?” And I want to go over some examples here of what you can use these methods for,

05:09 because I think they’re actually a really powerful concept, or a really powerful tool, for you to structure your code in a way that makes the most sense.

05:19 Okay. So, this is what I came up with: the classical pizza example for teaching object-oriented programming. So what I’ve done here is I defined this really simple Pizza class.

05:30 It’s got a constructor that takes some arbitrary ingredients object—we’re just going to assume it’s some kind of list or container with these ingredients—and then also put a .__repr__() on it so we can nicely format it as a string. And in here, if you’re wondering what that is—so, that is the new format strings in Python 3.6, which are really awesome, so I highly encourage you to try that out.

05:52 You could also just use regular format strings, of course. So, okay. Basically, what I did here, is I created this Pizza class and now we can use it to create Pizza objects, right? And so, if I’m not mistaken, that’s a Margherita?

06:07 Um…my wife’s Italian—you would probably kick my ass if I got that wrong—but I think that’s a Margherita. Well, what you’ve seen here is that we can create these Pizza objects, but as we create more and more complicated pizzas—ham, like a prosciutto or something, I don’t know. Maybe we need some mushrooms on that, as well.

06:33 And you can already tell I’m struggling with the naming here, right? I can create all of these wonderful pizzas here, but I need to remember all of these ingredients.

06:43 So now, it wouldn’t be too much of a stretch to actually solve this problem with a static method. Okay. So, I wanted to make it a little bit easier for us to create new Pizza objects without having to remember all of these ingredients.

06:56 A really good way to structure this, in my opinion, is to actually use class methods to have different factory functions for the different types of pizza you can create. I’m going to show you how this works in a minute now.

07:10 So, what I’m going to do here is I’m going to define a margherita() class method here,

07:17 and then that’s just going to create a new instance of the class…

07:23 and let me just type that out here. What I’m doing here is whenever this .margherita() method is called—and we can call it on just a Pizza class, we don’t actually need a real Pizza instance—I’m just going to create an instance of a Pizza or, you know, whatever the class is named. Like, the nice thing here is that I don’t have to refer to the name up here, so I can keep that name just in one place and whenever I update it I don’t have to worry about changing the rest of the code, but it’s just going to use the class object’s—it’s just going to call this .__init__() method, here, and it’s going to create a new Pizza with these ingredients.

08:07 And so, this is a really maintainable way to do these factory functions. I could also have a .prosciutto()I didn’t actually look the spelling up for this, all right?

08:20 So, if any Italians are watching this…

08:25 then let me know if I screwed this up!

08:29 So here, we actually want a cheese—I guess it would be 'mozzarella', right, and not 'cheese', but whatever—it’s a kind of cheese. Anyway, this isn’t about cheese—this is about Python. So, okay.

08:45 I’m creating a different kind of pizza here, and now when I finish defining this class, I can actually say, “Hey,

08:57 I want a Margherita,”

09:00 and that returns a new Pizza object, right? I could have also called this make_margherita() or new_margherita(), or something like that, right?

09:08 Just to kind of have a better naming scheme. But the same thing is going to work with the .prosciutto(). And I feel like this is a really good use for these class methods.

09:22 If you have classes with complicated constructors that take a lot of arguments and you want to provide a simplified interface for your users, then I think using a class method in this fashion can be really beneficial, and it’s just going to make the API a little bit easier for people to work with.

09:40 So, this is one example of where I would use a static method. I mean, of course, you could always argue that maybe this should be a separate function, yada yada yada, but I think in some cases this could really work well if you structure your classes that way. Up next, when to use static methods. So it’s a little bit hard to come up with a really simple example here, but you know, I’m going to keep stretching the pizza thing here. So, okay. This is what I came up with.

10:10 Basically, a static method doesn’t have access to the class or the object instance at all, right? And now, that’s a pretty big limitation, but it’s also a really good signal to show that a particular method is really independent from everything else around it, right? So for example, if I flag this as a static method, it’s pretty clear that this method, it’s probably not going to change the object or the class in any way, because it’s not really meant to have access to it. I mean, sure, you know, you could probably work around that and it could kind of work with a global variable, but in general, it’s a pretty good hint that this is a self-contained method, which has all kinds of benefits for later testing this thing and just kind of understanding the limitations of this method.

11:07 And so in some cases, it can really be helpful to make your code easier to maintain in the future, because you’re really communicating this intent where someone else reading this code can pretty clearly understand that in this case, our little ._circle_area() function here—it’s not going to modify the state of this object itself. So, let me walk you through this example real quick.

11:29 So basically, what I’ve added here—well, I kind of changed the constructor around a bit, so now we’ve got a radius argument here for the pizza as well.

11:38 I forgot to update the .__repr__(), but you know, it doesn’t really matter for now. And then I added this .area() function. And, well, I could have just calculated the area of the pizza directly in here, but I wanted to have a static method in there.

11:51 And so basically, what I did there instead of calculating the area directly with an expression here, I’m just offloading that to the ._circle_area() function, and then the ._circle_area() function just takes an r, which is the radius, and uses the classic r ^ 2 * pi formula to calculate the circle area.

12:13 So, you know, this is honestly kind of a simplistic example, and you usually wouldn’t implement it like that, but it goes to show the concept. And now what happens here is I can instantiate a Pizza.

12:27 So for example, this is a really sad pizza with like 4.5well, we don’t have a unit on that—let’s say meters. A giant pizza, cheese only. And we’re going to create this object, this Pizza object, and then we’re going to call the .area() function on it, and it’s going to give us this result.

12:48 So, the way this is calculated is that .area() actually forks off all that work to the ._circle_area() function, and now the ._circle_area() function is actually completely independent, and I also used a single underscore (_) to mark it as not part of the public API of this class, but kind of an internal implementation detail. But nevertheless, now the cool thing is that we have this ._circle_area() function that is completely independent from the rest of the object, and it can’t actually modify the object’s state.

13:26 This is a pretty good hint for someone else reading the code and it also simplifies testing because when I write my tests for this, then I don’t have to worry about instantiating a Pizza object, making sure it doesn’t get modified, because the ._circle_area() helper—it can’t do that. Right? And so I occasionally use static methods to communicate that intent and to keep my helper functions nice and clean and make sure that they’re not modifying the object’s state all over the place. Again, just a quick recap. So we talked about plain methods, class methods, and static methods, and when you can call them. Pretty much all you need to remember is that a regular method needs an object instance to be called on; and in a class method, it needs a class and it has access to the class; and a static method doesn’t really have any access to the object, or an object instance, or the class at all,

14:24 and it’s just kind of a way to namespace your functions. All right, so have fun exploring the pizza analogy here and building the perfect pizza API in an object-oriented fashion. Talk to you soon, and happy Pythoning!

varelaautumn on Sept. 23, 2020

These are interesting examples. I had tried looking up information on this topic before but most answers boil down to “never use static methods.”

But what you say seems far more helpful in that static method clearly signals a message of what the method can possibly do. I don’t see why people wouldn’t value static methods for this use.

The arguments against static methods always feel lazy and pedantic.

You must own this product to join the conversation.