What is the difference between class and instance attributes?

Learn what is the difference between class and instance attributes? with practical examples, diagrams, and best practices. Covers python, attributes, member-variables development techniques with vi...

Class vs. Instance Attributes in Python: A Comprehensive Guide

Hero image for What is the difference between class and instance attributes?

Understand the fundamental differences between class and instance attributes in Python, how they are defined, accessed, and when to use each for effective object-oriented programming.

In Python's object-oriented programming paradigm, attributes are data associated with classes or objects. A common point of confusion for new developers is distinguishing between class attributes and instance attributes. While both store data, their scope, ownership, and behavior differ significantly. Understanding these differences is crucial for writing robust, efficient, and maintainable Python code.

What are Instance Attributes?

Instance attributes are unique to each object (instance) of a class. They are defined inside a class's methods, typically within the __init__ constructor, using the self keyword. Each time a new object is created, it gets its own set of instance attributes, and changes to one instance's attributes do not affect other instances.

class Car:
    def __init__(self, make, model, year):
        self.make = make      # Instance attribute
        self.model = model    # Instance attribute
        self.year = year      # Instance attribute

car1 = Car("Toyota", "Camry", 2020)
car2 = Car("Honda", "Civic", 2022)

print(f"Car 1: {car1.make} {car1.model} ({car1.year})")
print(f"Car 2: {car2.make} {car2.model} ({car2.year})")

car1.year = 2021 # Modifying car1's instance attribute

print(f"Car 1 (modified): {car1.year}")
print(f"Car 2 (unaffected): {car2.year}")

Defining and accessing instance attributes

What are Class Attributes?

Class attributes are shared by all instances of a class. They are defined directly within the class body, outside of any method. Class attributes belong to the class itself, not to any specific instance. This means that if you modify a class attribute, the change will be reflected across all instances of that class, unless an instance has its own instance attribute with the same name (which then shadows the class attribute for that specific instance).

class Dog:
    species = "Canis familiaris" # Class attribute

    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

dog1 = Dog("Buddy", "Golden Retriever")
dog2 = Dog("Lucy", "Labrador")

print(f"Dog 1 species: {dog1.species}")
print(f"Dog 2 species: {dog2.species}")
print(f"Class species: {Dog.species}")

Dog.species = "Domestic Dog" # Modifying the class attribute

print(f"Dog 1 species (after modification): {dog1.species}")
print(f"Dog 2 species (after modification): {dog2.species}")
print(f"Class species (after modification): {Dog.species}")

dog1.species = "Wolf-like Dog" # This creates an instance attribute 'species' for dog1

print(f"Dog 1 species (instance attribute): {dog1.species}")
print(f"Dog 2 species (still class attribute): {dog2.species}")
print(f"Class species (unaffected by instance): {Dog.species}")

Defining and accessing class attributes, and shadowing

Visualizing the Difference

The following diagram illustrates how class attributes are shared across all instances, while instance attributes are unique to each instance.

classDiagram
    class MyClass {
        +class_attribute: string
        +instance_attribute_1: string
        +instance_attribute_2: string
        +__init__(val1, val2)
    }

    MyClass "1" -- "*" Instance

    Instance : +instance_attribute_1
    Instance : +instance_attribute_2

    note for MyClass "class_attribute is shared by all instances"
    note for Instance "instance_attribute_1 and instance_attribute_2 are unique to each instance"

Class and Instance Attributes Relationship

When to Use Which?

Choosing between class and instance attributes depends on the nature of the data you want to store:

  • Use Instance Attributes when:

    • The data is unique to each object.
    • Each object needs its own state.
    • Examples: name, age, color, price, status.
  • Use Class Attributes when:

    • The data is common to all instances of a class.
    • You need to store constants that apply to the entire class.
    • You want to track a count of instances created.
    • Examples: species, MAX_CAPACITY, number_of_instances.
class BankAccount:
    interest_rate = 0.015 # Class attribute: shared by all accounts
    account_count = 0     # Class attribute: tracks total accounts

    def __init__(self, owner, balance=0):
        self.owner = owner      # Instance attribute: unique to each account
        self.balance = balance  # Instance attribute: unique to each account
        BankAccount.account_count += 1 # Increment class attribute

    def deposit(self, amount):
        self.balance += amount

    def withdraw(self, amount):
        if self.balance >= amount:
            self.balance -= amount
        else:
            print("Insufficient funds!")

acc1 = BankAccount("Alice", 1000)
acc2 = BankAccount("Bob")

print(f"Account 1 owner: {acc1.owner}, balance: {acc1.balance}, interest rate: {acc1.interest_rate}")
print(f"Account 2 owner: {acc2.owner}, balance: {acc2.balance}, interest rate: {acc2.interest_rate}")
print(f"Total accounts: {BankAccount.account_count}")

BankAccount.interest_rate = 0.02 # Change class attribute

print(f"New interest rate for acc1: {acc1.interest_rate}")
print(f"New interest rate for acc2: {acc2.interest_rate}")

Practical example using both class and instance attributes