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

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 see our video player troubleshooting guide to resolve the issue.


00:00 In the previous lesson, I showed you how to write your first class in Python. In this lesson, I’ll dive deeper in how you associate data with your object through attributes.

00:10 So far, you’ve seen me use attributes on instantiated objects, but Python actually supports two different kinds of attributes: the instance ones that I just mentioned, and class attributes.

00:23 Instance attributes are specific to an object, while class attributes are shared across all objects created from the same class. Inside the class, the instance attributes are assigned and accessed using dot notation on the self reference, while class attributes are referenced directly on the class.

00:42 Let’s go look at some examples. I’m going to create a new class that counts how many instance objects are created from it, and I’m going to name it ObjectCounter.

00:58 Inside the class declaration block when I create an attribute this way, it’s a class attribute. That means it’ll be common across all object instances constructed from this class. Remember, to get at an instance inside the class, you need self, and there’s no self here, so it’s on the class instead.

01:19 Let’s write .__init__().

01:28 Each time you create a new object, .__init__() gets called. So this is a good place to increment the counter that counts how many instances there are. To access the class attribute, I use the name of the class instead of self, and then here I’m incrementing its value by one.

01:46 Let me instantiate an object …

01:52 and there it is. That ugly bit of REPL response is because I haven’t defined how to represent this object. The default display shows the name of the class, the module it’s in (__main__), and a reference address.

02:09 An object inherits the attributes of its class, so you can access the .num_instances attribute through the object. Let’s create two.

02:24 And the .num_instances counter has been incremented,

02:31 and as the value is on the class and not on the object, it’s shared across all the objects. Accessing it on one shows the same result as accessing it on two.

02:46 And now that I’ve shown you you can do that, I’m going to tell you that you shouldn’t. Generally speaking, I prefer to use the class name when accessing class attributes.

02:55 It makes it clearer that what you’re using is a class attribute, and it also avoids a subtle mistake. And what mistake is that you might ask. Good question. Follow along.

03:08 You’ve seen class attributes can be accessed through the object, and you saw that the class attribute is modified through the class, but it’s important to note that it can only be modified through the class.

03:22 You see, Python supports the dynamic addition of attributes to an object, not just in .__init__(), but anywhere you have access to the object.

03:32 Python also supports overriding values. When you assign an attribute on an object, Python sees this as being an object attribute. If you already have a class attribute with the same name, it gets overridden, hence the subtle bug.

03:48 You might think you’re changing the value of the shared attribute, but you’re not. You’re creating a new one on the object that happens to have the same name and overriding it.

03:58 Let’s go look at an example of this fiasco in practice. Same class as before. There’s my object. And like before, I can access the class attribute on the object. And

04:20 as a reminder, that’s the preferred way: doing it on the class itself. I mentioned I can assign attributes on objects dynamically. Here, I’ve added a new one called .value, and it’s an attribute just like anything else.

04:37 You don’t have to do it inside of .__init__() in order to assign a new attribute.

04:46 Here, I’ve done the exact same thing. I’ve created a .num_instances attribute and added a value dynamically. Since it has the same name as the class attribute, the object’s attribute takes precedence. So the class attribute hasn’t changed,

05:10 but the object attribute is 85. This would be why I prefer to use the class name. It’s clearer and avoids this problem. If you don’t have the class handy (say it’s declared in another file), you can get at an object’s class through the .__class__ attribute.

05:29 And as that’s the object’s class, you can get at its attributes from there. See? Although Python doesn’t stop you from accessing class attributes from the object, it’s a good habit to strictly access them from the class itself to avoid confusion. If you’re coming to Python from a strictly typed compiled object-oriented language, I suspect you’re thinking this is a giant foot gun.

05:55 I will admit on occasion I accidentally add a new attribute because I misspelled the attribute I wanted. A compiled language definitely catches these kinds of problems.

06:05 The other side of that coin, though, is some truly powerful things can be done with the dynamic nature of Python. A lot of the magic behind SQLAlchemy and Django’s ORM and how they map to databases is built on top of the dynamic nature of Python, so you gain some power at the risk of certain kinds of bugs.

06:24 Whether that’s worth that trade-off is for you to decide when you’re picking your language for your project. You’ve seen how classes and objects have attributes and methods.

06:35 In the next lesson, I’ll dive deeper to show you properties, Python’s equivalent of getters and setters.

Become a Member to join the conversation.