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

Unlock This Lesson

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

Unlock This Lesson

Use a Dunder Class Reference

00:00 I’ve just coded up .__str__() and I’ve used the .animal_type, which is a class attribute in my classes, but I’m accessing it through the instance.

00:09 So I want to show you what kind of troubles you can run into when you do that and also a solution for it.

00:16 Restart, and I will create the Dog, d again. Okay, of course now we’re running into an error because I added trait as a required parameter. So now it’s not just "brown" and called "Puppy", but it’s also not lazy at all.

00:35 It’s—what’s the opposite of lazy?—industrious.

00:42 Okay, so we have d.trait. We have our industrious brown dog Puppy, and if I print() the Dog now, the Dog instance, I should get back the string, and it says "The industrious brown dog Puppy jumps over the lazy dog." That’s the output that I was hoping for.

01:02 But now I want to redo the little problem that I did earlier. If I assign a new instance attribute on this Dog object that shadows, the class attribute .animal_type, then Python’s going to pick up that one first, and it’s not going to go back to the class attribute .animal_type.

01:20 So let’s say d.animal_type = "fox" and then print() the Dog object again.

01:28 Then you see that it says "The industrious brown fox Puppy jumps over the lazy dog", which is closer to the original sentence, but it’s not what we want.

01:36 I want to make sure that in this output, I always still know that the instance is actually a dog and not a fox, even if I would accidentally add this instance attribute that shadows the class attribute.

01:50 But I don’t want to go into each of those child classes and implement a new .__str__() method, where I then put in the .animal_type directly from the class.

02:04 So instead of self.animal_type or Animal.animal_type, I’m going to say self.__class__.animal_type, and this self.__class__ is going to always refer to the class that this instance was modeled from. In the Dog class, this is going to be Dog, and in the Pig class it’s going to be Pig, et cetera, so that the .animal_type is actually going to be the class attribute, even if you accidentally override it and put an instance attribute of the same name in there. Long story short, let’s try it out.

02:43 Again, we need our "Puppy".

02:49 Try it out first. print(d) still working. "The industrious brown dog Puppy jumps over the lazy dog." Great. And then if I create this instance attribute, .animal_type, that shadows the class attribute, now if I print(d),

03:04 you can see that it still prints out dog. It doesn’t take the instance attribute .animal_type, but it still refers back to the class attribute.

03:13 So, a little hoop that I jumped through here just to make sure that I can also print() out what type of animal an animal is and to keep this class attribute around without having to do a lot of code duplication.

03:27 There might be other ways to solve this, but I think this works well, and it was a nice chance to talk about some potential issues with class attributes. All right, Sheep, Dog, and Pig can all have .name, .color, and .trait and can print() out nicely. Next, we also need some methods.

Become a Member to join the conversation.