When diving into Python's object-oriented programming, one of the pivotal concepts you'll encounter is the difference between instance attributes and class attributes. Understanding these distinctions is crucial for writing clean, efficient, and bug-free code. This lesson will guide you through these concepts with practical examples, detailed explanations, and helpful analogies.
What Are Attributes in Python Classes?
Attributes are variables that belong to an object or a class. They represent data or properties associated with that object or class. In Python, these attributes can be attached to either the class itself or to individual instances (objects) of the class.
Before exploring their differences, let's clarify the two main types of attributes:
- Class Attributes: Variables that are shared across all instances of a class.
- Instance Attributes: Variables that are unique to each instance of a class.
Understanding Instance Attributes
Instance attributes are defined within methods, usually the __init__ constructor, and are prefixed by self. Each object created from the class has its own copy of instance attributes. This means changing the attribute in one instance does not affect others.
📌 Deep Dive: Creating and Using Instance Attributes
class Dog:
def __init__(self, name, age):
self.name = name # Instance attribute
self.age = age # Instance attribute
# Create instances
dog1 = Dog("Buddy", 3)
dog2 = Dog("Lucy", 5)
print(dog1.name) # Buddy
print(dog2.name) # Lucy
# Changing dog1's name won't affect dog2
dog1.name = "Max"
print(dog1.name) # Max
print(dog2.name) # Lucy
Lucy
Max
Lucy
In this example, name and age are instance attributes. Each Dog object stores its own name and age. Modifying one dog's name does not impact the other.
What Are Class Attributes?
Class attributes are variables defined directly inside a class but outside any methods. These attributes are shared by all instances of the class. This means if you change a class attribute, the change is reflected across all instances unless an instance specifically overrides it.
📌 Deep Dive: Using Class Attributes
class Dog:
species = "Canis familiaris" # Class attribute
def __init__(self, name, age):
self.name = name
self.age = age
dog1 = Dog("Buddy", 3)
dog2 = Dog("Lucy", 5)
print(dog1.species) # Canis familiaris
print(dog2.species) # Canis familiaris
# Change class attribute
Dog.species = "Canine"
print(dog1.species) # Canine
print(dog2.species) # Canine
Canis familiaris
Canine
Canine
Here, species is a class attribute shared by all Dog instances. Changing it on the class reflects on all objects.

Key Differences Between Instance and Class Attributes
Let's break down the main distinctions between these two attribute types in a clear comparison:
| Aspect | Instance Attributes | Class Attributes |
|---|---|---|
| Definition | Defined inside methods, usually __init__, using self | Defined directly inside the class, outside any method |
| Scope | Unique to each object or instance | Shared by all instances of the class |
| Changing Value | Affects only the specific instance | Affects all instances unless overridden in an instance |
| Storage | Stored in the instance's __dict__ | Stored in the class's __dict__ |
| Use Case | Properties that vary per object, e.g., a person's name | Properties common to all instances, e.g., species, default settings |
How Python Looks Up Attributes
When you access an attribute on an instance, Python follows a specific order to find its value:
- Look for the attribute in the instance's namespace (
__dict__). - If not found, look in the class's namespace.
- If still not found, look in parent classes (if inheritance is involved).
💡 Attribute Lookup Analogy
Think of attribute lookup like searching for a book in your personal bookshelf (instance). If it's not there, you check the shared community library (class). If you still can't find it, you explore other connected libraries (parent classes).
Overriding Class Attributes in Instances
An instance can override a class attribute by defining an attribute with the same name. This creates a new entry in the instance's namespace, shadowing the class attribute.
📌 Deep Dive: Overriding Class Attributes
class Dog:
species = "Canis familiaris" # Class attribute
def __init__(self, name):
self.name = name
dog1 = Dog("Buddy")
dog2 = Dog("Lucy")
print(dog1.species) # Canis familiaris
print(dog2.species) # Canis familiaris
# Override class attribute in dog1 instance
dog1.species = "Wolf"
print(dog1.species) # Wolf (instance attribute)
print(dog2.species) # Canis familiaris (class attribute)
Canis familiaris
Wolf
Canis familiaris
As shown, dog1 has its own species attribute now, different from the class attribute shared by other instances.
When to Use Instance Attributes vs Class Attributes?
Choosing between instance and class attributes depends on whether the value should be unique per object or shared among all objects.
- Instance attributes: Use these for data that varies from object to object, such as a person's name, an animal's age, or a vehicle's color.
- Class attributes: Use these for data common to all instances, like default settings, species names, or constants related to the class itself.
Common Pitfalls
⚠️ Beware of Mutable Class Attributes
Mutable objects (like lists or dictionaries) as class attributes can lead to unexpected behavior because changes affect all instances sharing that attribute. Always be cautious when using mutable class attributes. If you want each instance to have its own copy, initialize it inside __init__ as an instance attribute.
📌 Deep Dive: Mutable Class Attribute Pitfall
class Cat:
toys = [] # Class attribute (mutable)
def __init__(self, name):
self.name = name
cat1 = Cat("Whiskers")
cat2 = Cat("Felix")
cat1.toys.append("Ball")
print(cat1.toys) # ['Ball']
print(cat2.toys) # ['Ball'] <-- shared list!
# Fix: use instance attribute for toys
class CatFixed:
def __init__(self, name):
self.name = name
self.toys = [] # Instance attribute
cat3 = CatFixed("Mittens")
cat4 = CatFixed("Tom")
cat3.toys.append("Mouse")
print(cat3.toys) # ['Mouse']
print(cat4.toys) # []
['Ball']
['Mouse']
[]
Notice how modifying the class attribute toys affects all instances, which is usually undesirable.
Summary: Mastering Attributes for Better Python Classes
Let's recap the essential points you should remember:
- Instance attributes are unique per object, usually defined within
__init__usingself. - Class attributes are shared among all instances and defined directly in the class body.
- Python looks for attributes first in the instance, then the class, enabling instance attribute overrides.
- Mutable class attributes can cause shared-state bugs; prefer instance attributes for mutable data.
- Use class attributes for constants or default values common to all objects.
Grasping these concepts allows you to design Python classes that behave predictably and efficiently, enhancing code readability and maintainability.
Quick Knowledge Check
Test what you just learned
Question 1 of 2
If you want every object of a class to share the same value for a certain attribute, where should you define it?
Question 2 of 2
What is a common problem when using mutable objects (like lists) as class attributes?
Loading results...