Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
117: Instance Attributes vs Class Attributes
I see this a lot when I'm reviewing code from developers moving into Python: they treat variables defined at the top of a class as if they are just "default values" for every instance. They think that by putting a list or a dictionary there, they're giving each object its own starting point. It feels intuitive, but in Python, it's a recipe for a very frustrating afternoon of debugging.
The "Default Value" Trap
Let's look at a classic mistake. Imagine you're building a system to track employees and the projects they're working on. You might be tempted to write it like this:
class Employee:
projects = [] # I'm just setting a default empty list here, right?
def __init__(self, name):
self.name = name
def add_project(self, project_name):
self.projects.append(project_name)
alice = Employee("Alice")
bob = Employee("Bob")
alice.add_project("Website Redesign")
print(f"Alice's projects: {alice.projects}") # ['Website Redesign']
print(f"Bob's projects: {bob.projects}") # ['Website Redesign'] <-- Wait, what?
You'll notice that Bob suddenly has Alice's project. Why? Because projects = [] is a class attribute. It exists in one place—the class definition itself—and is shared by every single instance of that class. When you called self.projects.append(), you weren't modifying a list belonging to Alice; you were modifying the one list that the entire Employee class shares. I've seen this bug crash production systems because a developer thought they were updating a user's session, but they were actually updating a global state for every user on the site.
Ownership: The Class Namespace vs. The Instance Namespace
To get this right, you have to understand that Python looks for attributes in a specific order. When you ask for self.projects, Python first looks at the individual instance (the self). If it doesn't find it there, it looks "up" at the class definition.
In the example above, self.projects didn't exist on the instance, so Python found the class attribute and modified it. But here is where it gets weird: if you assign a value to self.projects, Python creates a new attribute on the instance that "shadows" the class attribute.
class Employee: company_name = "TechCorp" # Shared by everyone def __init__(self, name): self.name = name # Unique to each person # Both refer to the class attribute alice = Employee("Alice") bob = Employee("Bob") print(alice.company_name) # TechCorp print(bob.company_name) # TechCorp # This creates an INSTANCE attribute for Alice only alice.company_name = "Freelance" print(alice.company_name) # Freelance (Instance attribute) print(bob.company_name) # TechCorp (Still using Class attribute)The rule of thumb I follow is simple: if the data should be unique to the object (like a name, a balance, or a list of tasks), it must be defined inside
__init__usingself. If the data is a constant or a shared state that every object should see and potentially share (like a tax rate or a global configuration), put it at the class level.
- Class Attributes: Defined outside
__init__. Shared by all instances. Great for constants or tracking global state (like a total count of objects created). - Instance Attributes: Defined inside
__init__. Unique to each instance. Essential for the "state" of a specific object.
📋 Practical Task
Fixing the Galactic Cargo Manifest Bug
You've been handed a piece of code for a space freighter system. The lead engineer tried to use a class attribute to store the cargo_manifest for each ship, but now every ship in the fleet is sharing the same cargo! When the S.S. Enterprise loads "Dilithium Crystals", the Millennium Falcon suddenly has them too.
Your Task: Modify the Spaceship class so that each ship has its own independent cargo manifest, while keeping the galaxy_name as a shared class attribute (since all ships are in the same galaxy).
class Spaceship:
galaxy_name = "Andromeda"
cargo_manifest = [] # This is the bug!
def __init__(self, ship_name):
self.ship_name = ship_name
def load_cargo(self, item):
self.cargo_manifest.append(item)
# Test your fix here:
ship1 = Spaceship("Voyager")
ship2 = Spaceship("Discovery")
ship1.load_cargo("Medical Supplies")
ship2.load_cargo("Fuel Cells")
print(f"{ship1.ship_name} cargo: {ship1.cargo_manifest}")
# Expected: ['Medical Supplies']
print(f"{ship2.ship_name} cargo: {ship2.cargo_manifest}")
# Expected: ['Fuel Cells']
There are no comments for now.