py4u blog

Python Class Variables: Why Did Changing Parent Class Affect Subclass? (Common Misuse Explained)

If you’ve ever worked with Python classes and inheritance, you might have encountered a puzzling scenario: you modify a class variable in a parent class, and suddenly the subclass that inherits from it starts behaving unexpectedly. The subclass’s “own” class variable seems to change too—even though you never touched the subclass directly!

This isn’t a bug in Python; it’s a result of how class variables and attribute lookup work in inheritance hierarchies. In this blog, we’ll demystify this common confusion, explain why modifying a parent class affects its subclasses, and show you how to avoid this pitfall in your code.

2026-01

Table of Contents#

  1. Understanding Class Variables in Python
  2. Inheritance and Class Variables: The Basics
  3. The Common Pitfall: Modifying the Parent Affects the Subclass
  4. Why Does This Happen? The Attribute Lookup Chain
  5. Mutable vs. Immutable Class Variables: A Deeper Dive
  6. How to Avoid This: Making Subclass Variables Independent
  7. Best Practices for Class Variables in Inheritance
  8. Conclusion
  9. References

1. Understanding Class Variables in Python#

Before diving into inheritance, let’s clarify what class variables are and how they differ from instance variables.

What Are Class Variables?#

A class variable is a variable defined in a class (outside any method, including __init__). It is shared across all instances of the class and the class itself. Unlike instance variables (which are unique to each instance), class variables belong to the class object, not individual instances.

Example: Class Variable Basics

class Parent:
    # Class variable: shared across all instances and subclasses
    class_var = "I'm a class variable"
 
# Access class variable via the class
print(Parent.class_var)  # Output: I'm a class variable
 
# Access via an instance
obj = Parent()
print(obj.class_var)  # Output: I'm a class variable
 
# Modify via the class (affects all instances/subclasses referencing it)
Parent.class_var = "Updated class variable"
print(obj.class_var)  # Output: Updated class variable

Here, class_var is defined at the class level and is shared by the Parent class and all its instances.

Class Variables vs. Instance Variables#

AspectClass VariableInstance Variable
DefinitionDefined outside __init__ (class level)Defined inside __init__ (instance level)
ScopeShared across all instances/subclassesUnique to each instance
AccessClass.variable or instance.variableinstance.variable

2. Inheritance and Class Variables: The Basics#

In Python, subclasses inherit attributes (including class variables) from their parent classes. By default, if a subclass doesn’t explicitly define a class variable, it will “borrow” the parent’s class variable.

Example: Inheriting Class Variables

class Parent:
    class_var = "Parent's class variable"
 
class Child(Parent):
    # No explicit class_var defined; inherits from Parent
    pass
 
# Child inherits class_var from Parent
print(Child.class_var)  # Output: Parent's class variable

At first glance, this seems intuitive: Child reuses Parent’s class_var because it hasn’t defined its own. But this inheritance can lead to unexpected behavior when the parent’s class variable is modified.

3. The Common Pitfall: Modifying the Parent Affects the Subclass#

The confusion arises when you modify the parent’s class variable and suddenly notice the subclass’s “version” of the variable has also changed. Let’s demonstrate this with an example:

Example: The Unexpected Side Effect

class Parent:
    count = 0  # Class variable
 
class Child(Parent):
    pass  # Inherits count from Parent
 
# Initial state: Child.count is Parent.count
print("Child.count initially:", Child.count)  # Output: Child.count initially: 0
 
# Modify Parent's class variable
Parent.count = 1
 
# Now check Child.count...
print("Child.count after modifying Parent:", Child.count)  # Output: Child.count after modifying Parent: 1

Wait a minute! We never touched Child.count, but it changed when we updated Parent.count. Why?

4. Why Does This Happen? The Attribute Lookup Chain#

To understand this, we need to explore how Python resolves attribute access for classes. When you access Child.class_var, Python follows a strict attribute lookup chain:

  1. First, check if Child has its own class_var defined in Child.__dict__ (the class’s namespace dictionary).
  2. If not found, Python climbs the inheritance hierarchy and checks the parent class (Parent) for class_var in Parent.__dict__.
  3. This continues up to object (the root of all Python classes) until the attribute is found or an AttributeError is raised.

In the example above, Child never defines its own count variable, so Child.count is just a reference to Parent.count. Thus, modifying Parent.count directly changes the value Child.count points to.

Proof with __dict__
We can verify this by inspecting the __dict__ attribute of Child and Parent:

print("Child.__dict__ before modification:", Child.__dict__)  
# Output: Child.__dict__ before modification: {'__module__': '__main__', '__doc__': None}  
# Notice: No 'count' key in Child.__dict__
 
print("Parent.__dict__['count'] before modification:", Parent.__dict__['count'])  
# Output: Parent.__dict__['count'] before modification: 0
 
Parent.count = 1
 
print("Parent.__dict__['count'] after modification:", Parent.__dict__['count'])  
# Output: Parent.__dict__['count'] after modification: 1
 
# Child still has no 'count' in its __dict__, so it uses Parent's
print("Child.count after modification:", Child.count)  # Output: 1

Since Child has no count in its own __dict__, it relies on Parent’s count.

5. Mutable Class Variables: An Even Trickier Case#

The problem becomes more subtle with mutable class variables (e.g., lists, dictionaries). Modifying the contents of a mutable class variable (instead of reassigning it) will affect all classes and instances that reference it—even if the subclass later defines its own variable!

Example: Mutable Class Variables

class Parent:
    items = []  # Mutable class variable (list)
 
class Child(Parent):
    pass
 
# Add an item via Parent
Parent.items.append("Parent item")
print("Child.items after Parent append:", Child.items)  # Output: Child.items after Parent append: ['Parent item']
 
# Now, define Child's own items (too late for the existing data!)
Child.items = []
print("Child.items after defining Child's own:", Child.items)  # Output: [] (new empty list)
 
# But Parent's items are unchanged
print("Parent.items now:", Parent.items)  # Output: ['Parent item']

Here, Child.items initially references Parent.items (a mutable list). Appending to Parent.items modifies the shared list, so Child.items sees the change. Only when Child explicitly defines its own items does it get a separate list.

6. How to Avoid This: Making Subclass Variables Independent#

To prevent the parent class from affecting the subclass, the subclass must explicitly define its own class variable. This breaks the link to the parent’s variable by adding the variable to the subclass’s __dict__.

Solution 1: Explicitly Define the Class Variable in the Subclass#

For immutable class variables (e.g., integers, strings), simply re-declare the variable in the subclass:

class Parent:
    count = 0
 
class Child(Parent):
    count = Parent.count  # Explicitly define Child's own count (copies initial value)
 
# Modify Parent's count
Parent.count = 1
 
# Child's count is now independent
print("Child.count after Parent modification:", Child.count)  # Output: 0 (unchanged!)

Now, Child.__dict__ contains count, so Child.count no longer references Parent.count.

Solution 2: Copy Mutable Class Variables#

For mutable variables (e.g., lists, dictionaries), use a copy to avoid sharing the same object:

class Parent:
    items = [1, 2, 3]  # Mutable list
 
class Child(Parent):
    # Create a new list (copy of Parent.items) instead of referencing the same list
    items = list(Parent.items)  # Shallow copy; use copy.deepcopy() for nested structures
 
# Modify Parent's items
Parent.items.append(4)
 
# Child's items remain independent
print("Child.items:", Child.items)  # Output: [1, 2, 3] (unchanged)
print("Parent.items:", Parent.items)  # Output: [1, 2, 3, 4] (modified)

7. Best Practices for Class Variables in Inheritance#

To avoid accidental side effects with class variables and inheritance:

  1. Explicitly Initialize Subclass Variables: Always define class variables in subclasses if they should be independent of the parent.
  2. Avoid Mutable Class Variables in Inheritance: Mutable variables (lists, dicts) are prone to unintended sharing. Use immutable types (int, str) or instance variables instead when possible.
  3. Document Inherited Class Variables: Clearly note if a subclass is intended to inherit and share a parent’s class variable (e.g., for shared configuration).
  4. Use @classmethod for Class-Level Logic: If you need to modify class variables, encapsulate the logic in a class method to avoid direct external modification.

8. Conclusion#

The confusion arises because subclasses inherit class variables from parents by reference, not by value—unless the subclass explicitly defines its own variable. When you modify a parent’s class variable, any subclass that hasn’t overridden it will reflect the change, thanks to Python’s attribute lookup chain.

By explicitly defining class variables in subclasses (and copying mutable objects when needed), you can ensure independence between parent and subclass variables. Understanding this behavior is key to writing predictable, bug-free Python code.

9. References#