Explore Class Attribute Quirks
00:00 I want to know what type of animal each animal is, and I want to keep track of that inside of the class. This sounds like a good idea to use a class attribute for it because all dogs are going to be dogs, right?
00:14
So one way that you could go is just define the class attribute in here. I’m going to say .animal_type
and say that every instance of this Dog
class is going to have the .animal_type
"dog"
and do the same for Pig
and Sheep
, only those are going to be pigs and sheep.
00:39
So I’m going to do something maybe controversial, I’m not sure, but I actually want to show you something about that. I’m going to add an .animal_type
of None
to the parent class because I want to show you something about the attribute lookup order that might be a little tricky when you’re working with class attributes.
00:57 Let’s go ahead and run this, and then I can show you what I mean.
01:03
Press F5. I can now create an Animal
. I can create a Dog
, and it needs a name and the color. So this one’s going to be "Puppy"
and "brown"
.
01:20
And now I have access to the attribute .animal_type
through the instance. So I’m accessing the class attribute .animal_type
through the Dog
instance that I just lazily called d
here.
01:33
It’s interesting to see that we have .animal_type
also in the parent class, but it gets overridden by the class attribute in the child class.
01:43
That looks good. But what is interesting here is that if you change, maybe accidentally, or I don’t know why you would do it intentionally, but it could happen that you say d.animal_type
equals something else. Let’s say this is a "fox"
.
02:00
This doesn’t change the class attribute. The class attribute is still "dog"
also for this Dog
, but because of the attribute lookup order in Python, it means that now if you look for d.animal_type
in the same way that it did before, you’re going to get "fox"
back because you created a new instance attribute of .animal_type
that shadows the class attribute .animal_type
.
02:25
So this can be a little bit confusing right now you have d.animal_type
pointing to "fox"
, and you do still have access to the class attribute as well, but you have to access it using .__class__
.
02:39
And there you can see that this is still a "dog"
. However, if you write some code that works with the class attribute, but accessing it from the instance, then you might accidentally override it and kind of run into trouble there.
02:52 So one good way to go is that you usually access the class attributes through the class,
03:00 but the way that I’m envisioning this print out here, Maybe I can show you some more of what types of troubles you could run into here. So this is not necessarily a bad thing to do.
03:10
I think it makes sense for this design. I want to have a class attribute on Dog
that just points to "dog"
, and every Dog
instance should be a "dog"
. So I’m pretty happy with this design, but just keep in mind that there’s this attribute lookup order that first looks at the instance and then looks at the first class and then goes upwards looking at all the parent classes.
03:32
It searches for .animal_type
being defined anywhere there, and the first one that it finds, this is what it’s going to give you back on the instance.
03:39
This is why here you get when you type d.animal_type
, after assigning it to "fox"
, you’re going to get "fox"
instead of earlier getting "dog"
, because it finds it in the instance already after you’ve created this .animal_type
instance attribute. And then it doesn’t look for the class attribute anymore.
03:57 Long story short, this is a subjective design decision now and comes with some gotchas, but in this case, it also comes with some benefits. I like the idea of having the class attribute defining what type of animal this is. So I’m going to keep it like that.
04:15
So what to do next? Well, the printout of the instance isn’t very pretty. So let’s build a .__str__()
for all of these classes.
Become a Member to join the conversation.