课程: Python Essential Training

The anatomy of a class - Python教程

课程: Python Essential Training

The anatomy of a class

- Oh, hello there. Class can be a confusing subject. What is it? How can we tell who's a member of which class? We're often told simply that we should never talk about it. As my dear friend Guido van Rossum, the creator of Python, told me at a party last week, now is better than never. Let's go to the code. Here we have a class that we made previously, the dog class. It has two instance attributes, name and legs, the number of legs on the dog. We call them instance attributes because they're attributes that each instance of the dog class has. So if I make a new instance, my dog, I'll give it the name Rover, print my dog dot name, print my dog dot legs. So great, it has the name and legs attributes. But notice that even though this value is hard coded in the dog initialization function, we can't see what that value is directly. So if we say dog.legs, we get an error, but we can't really change the value of legs. So what if we decide that having four legs is an intrinsic property of being a dog? Of course, adorable three-legged dogs exist, and we can make a class for them too. But for now, let's make legs an attribute of the class itself. We can do that by moving it outside of this initialization function of the constructor. We just define it up here and get rid of it down here. And notice that each instance still has this value as always, but now if we call dog.legs, we can see it on the class itself. Programmers call these static attributes or static variables, in the sense that they're unchanging with each instance. They're not dynamic, they're static. Traditionally, static variables are used to hold constants and fundamental business logic and that sort of thing. But be careful. Because just like any variable, you can reset it on the class. You can set the value of legs to something else. Now, if we make a new dog. Rover is one of those adorable little three-legged doggies. (dog whimpering) So the convention to prevent this sort of thing is to add an underscore before the variable name. Note that this doesn't actually stop anyone from messing with the variable, but the underscore is just an indicator or a warning, mess with this at your own risk because you could break things by changing the value. The underscore also has another connotation, that the user shouldn't rely on or even reference the values directly. These private variables are implementation details, subject to change without notice, don't look at them. In this case, we might use what's called a getter function or a get method or a getter method for the legs. So let's call this get legs. Getter methods always start with get, self, return self dot underscore legs. Oh, and we need to use that here. Get legs. Okay, great. Now, strictly speaking, we don't need to pass the self attribute into this getter function. Remember, self is an object instance that's literally the same instance we're calling the function on. So self is my dog, right? Same value. But legs is a static variable in this class, right? So we could rewrite "get number of legs" as this, and now we don't actually need self. The problem is that now we can't call this method like we're used to. It'll throw an error. And it's complaining because when we call a method on a class instance, like my dog dot get legs, the class instance, my dog, gets passed in as the first value automatically, and saying zero positional arguments but one was given. And that's because we're passing this invisible value in there. We could still call the function like this, dog dot get legs, but that's just not very intuitive. It looks a little odd. In my opinion, the best way to do this is like this, and just call it in the traditional way. Also note that classes have their own variable scope rules that are very similar to the variable scope rules that we discussed with functions previously. So self dot legs, if it's not set to something else, references the class variable legs. However, we could also do something like this. My dog dot legs equals three and print dog dot legs. Okay, so you can see that we're changing the instance variable, underscore legs, but not the class variable. That remains the same. As you can see, there are a lot of options when creating a class. Writing a class is like creating user interface for code. You need to think about how the class will be used, what the user's expectations are, what's most convenient, whether variables should be accessed directly, or should or should not be modified. Even if you know that you're the only person who will ever use this code, be careful. I've cursed many badly written classes, only to discover that the author was me three years ago. (slurps)

内容