Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Decorating

00:00 In the previous lesson, I showed you how to ask the user for input and how to open files to get at their data. This lesson is the beginning of a new section all about built-in functions that support object-oriented coding in Python. Like I mentioned in the lesson on functional programming, Python’s a bit of a mishmash of a language, supporting many different programming paradigms.

00:22 Object-oriented programming capabilities were added early in the days of Python 2, so they’ve been around for a while. Object-oriented programming revolves around class declaration and objects.

00:33 A class is a way of grouping data and the methods on that data. For example, if you were representing a car in your code, you might have a class that had attributes for their make and model, as well as their current mileage.

00:44 Then you could have a method that calculates the distance until their next oil change. There’s a whole family of built-in functions that help you write object-oriented code, including functions that you use during the declaration of methods to alter their behavior, interact with pieces of data in an object, introspect parts of the class, or to build or change how classes are built.

01:08 I’m going to start out with the first group. These are functions typically used as decorators to change how methods work. This is going to get a little strange, because they are functions, but you’re not going to actually call them because you’re going to use them as decorators.

01:23 You’ll see what I mean in a second.

01:25 Let’s start with the built-in property() function. This gets used as a decorator on methods in a class to make the method work like an attribute, meaning you can just access it rather than invoke it like a callable.

01:38 Consider a class representing a Cartesian point. I’m going to be doing a bit of math here, so I’ve got to get the math module first. Now I’ll define my class.

01:49 I’m doing a two-dimensional point, so it takes x and y coordinates as arguments,

01:57 which I then store away.

02:01 Say I want to calculate the distance from the origin. I could write that as a method, but by using the property() function as a decorator, I can make it look more like an attribute.

02:14 The method I’m doing here is from_origin. @property, although it’s a built-in function, is being used as a decorator here, so you don’t put parentheses on it.

02:24 You’re not calling it, you’re referencing it.

02:32 And that’s just the math to calculate the distance from the origin. Let’s test it out. First I’ll create an instance.

02:42 There’s x. There’s y. And there’s the distance from_origin. Since I used the property decorator, I didn’t have to invoke from_origin as a callable.

02:54 This is really just a design choice. Sometimes you can do it this way, sometimes you would do it as a method. For this particular example, I could argue it either way. One of the places I’ve seen this pop up is when you originally had an attribute, and then it turned out that you needed to have a side effect when you touched the attribute. In that case, you can change the attribute into a property method, and the code is still backwardly compatible because it looks like calling any other attribute. On to the next decorator.

03:25 Regular old methods on a class require an instance of the object to invoke them. They’re associated with the object itself rather than the class. These kinds of methods are known as instance methods, but seeing as they’re the vast majority of methods used, they often get shortened to just methods.

03:43 Sometimes, though, you want methods that are associated with the class rather than the instance object. Logically enough, these are called class methods.

03:51 You declare a class method by using the built-in classmethod() function as a decorator. I’m going to rewrite my Point class as a new example.

04:07 Starting out the same as before.

04:16 And like with the property() function, you decorate a class method without invoking the function itself, so no parentheses. A common use of class methods is an object factory. Normally, when you construct a point, you pass in the x and y coordinates.

04:31 If you want to create a point based on polar coordinates, you have to do some math on those first. This method’s going to do that math and return a new point object, hence a factory.

04:42 Before I show you the math, one thing to note here is about the first argument to the class method. You know how the first argument to a regular instance method is self?

04:52 Well, technically you can name the first argument whatever you want, but the convention is to use self for instance methods. For instance methods, the first argument is a reference to the object.

05:03 Python passes that in for you when you call it. Class methods are different. There is no instance, so the first argument is a reference to the class itself.

05:14 Convention is to name this cls. You can’t spell the word out fully because C-L-A-S-S is a keyword in Python, and that’ll muck up the parser. Since I want to return an instance object, I’m going to call Point’s constructor, invoking it with parentheses.

05:31 Or, sort of.

05:35 Instead of calling Point directly, I’m going to use the class argument. Remember, when you instantiate Point, you’re referencing the class.

05:43 That same class is what’s getting passed in here. So why would I do it this way? Well, you could put Point here, it would work, but if you rename the class, well then you’d have to remember to rename the thing inside of this method.

05:57 By using cls instead, the argument that’s passed in, it doesn’t matter what Point is named. So this is a little safer, a little more future-proof.

06:07 Since cls is really just calling the constructor, I’m going to need two arguments, the x and y coordinates.

06:19 That messy bit of math converts our distance and angle values of a polar coordinate into the x coordinate. And that’s for the y value. Let’s try out our new factory method.

06:40 Because it’s a class method, you call it directly on the class itself. Since our class method is a factory, it returned a new point instance object, which of course has an x and y values.

06:53 Class methods are included on the object instance, so I can use the p instance object to create a new one as well.

07:02 You don’t normally do this. It’s clearer to use the class because it’s a class method, but all instance objects inherit their class methods, so you can. The end result is a new instance object,

07:16 having x and y coordinates, of course.

07:20 Instance methods take the object instance as their first argument, self. Class methods take a reference to the class as the first argument, that’s cls. There’s a third kind, static methods. These don’t take an instance or class as their first argument. The only arguments they take are the ones you define, which of course means you can’t get at the object or class within the method.

07:45 But sometimes that’s okay. This technique is usually used to group helper functions together. You’re putting them inside of a class to show that they’re related, but there’s no associated data.

07:56 Let’s create a text formatting helper.

08:01 Declare the class using the built-in function @staticmethod as a decorator.

08:07 And like I said, since this is a static method, there’s no self or class arguments, only the ones I’m going to use, which in this case is the number I’m going to format.

08:21 This is probably obvious, but I’m going to say it anyways, just to hit it home. Since there’s no self or class, the only things you can reference in this function are the arguments passed in.

08:31 Here, I’m using an f-string to format the value as a two-digit decimal with a dollar sign as a prefix.

08:42 $3.14 seems like an appropriate price for a slice of lemon meringue. I can count on one hand the number of times I’ve used a static method in Python. My preference would be to just use functions in a module instead. But this tool is there if you like.

08:59 Python has a powerful tool called the descriptor protocol, which allows you to define setters and getters and side-effect behavior on objects. This video course and tutorial starts you off with the built-in property() function, then goes even deeper behind the scenes on how all of that works.

09:16 Or, if you’d like to see more examples of class and static methods, you could check out this course or tutorial. Let’s go from methods to attributes. Next up, you’ll learn about the functions that let you manage the data aspects of an object.

Become a Member to join the conversation.