Skip to main content

Constructors, Destructors, and Attributes

Now that you can create basic classes, you'll dive deeper into sophisticated patterns: constructors with flexible parameters, the critical distinction between class and instance attributes, and object cleanup with destructors.

In this lesson, you'll discover why these patterns matter through hands-on experiments, learn from AI explanations, challenge the AI with subtle edge cases, and build a comprehensive reference guide.


Part 1: Experience Default Parameters and Attribute Types

Your Role: Code experimenter discovering initialization patterns and attribute scoping

Discovery Exercise 1: Why Default Parameters Matter

Create attribute_experiments.py and run this:

Loading Python environment...

Now add a default parameter:

Loading Python environment...

💬 AI CoLearning Prompt

"I added a default parameter age=0 to my Dog class. Now I can create dogs with or without age. But when should parameters be required vs have defaults? Show me 3 examples: 1) user registration (which fields must be required?), 2) database connection (which have sensible defaults?), 3) API client config. Explain the design principle."

Expected Understanding: AI will explain that critical data should be required (no default), convenience data can have defaults. You'll see the tradeoff between flexibility and enforced completeness.


Discovery Exercise 2: Instance vs Class Attributes

Run this experiment:

Loading Python environment...

💬 AI CoLearning Prompt

"I have Dog class with species (class attribute) and name (instance attribute). When Dog.species changes, ALL dogs see it. When dog1.name changes, only dog1 is affected. Explain:

  1. What's stored in memory differently for class vs instance attributes?
  2. Give me 3 real-world examples where class attributes make sense (like API base_url, database connection pool, configuration)
  3. When should I avoid class attributes?"

Expected Understanding: AI will explain that class attributes are shared memory (one copy), instance attributes are per-object memory (N copies). Use class attributes for configuration, constants, shared state. Avoid for mutable data that should be independent.


Discovery Exercise 3: The Shadowing Trap

Run this and observe strange behavior:

Loading Python environment...

💬 AI CoLearning Prompt

"I tried to update a class attribute through an instance (c1.count = 5) but it created an INSTANCE attribute instead, shadowing the class attribute. Now c1.count and c2.count are different!

  1. Why does Python allow this? Is it a bug or feature?
  2. How do I update class attributes correctly? (Hint: use ClassName.attribute)
  3. Show me how to detect if an attribute is instance or class using dict"

Expected Understanding: AI will explain that obj.attr = value ALWAYS creates instance attribute. To modify class attributes, use ClassName.attr = value. Shadowing is intentional but often confusing. Use hasattr() and __dict__ for inspection.


Your Discovery Summary

Instead of manual files, use AI to synthesize:

💬 AI CoLearning Prompt

"Based on my experiments with default parameters, instance/class attributes, and shadowing, summarize these key insights:

  1. When should constructor parameters be required vs have defaults?
  2. When should data be instance attributes vs class attributes?
  3. What's the shadowing trap and how do I avoid it?

Give me 3 bullet points for my reference guide."

Deliverable: Save AI's synthesis in your notes. You've discovered constructor design patterns—now you're ready to learn advanced techniques.


Part 2: Learn Advanced Constructor Patterns

Your Role: Student receiving instruction from AI Teacher

AI Teaching Prompt

Ask your AI companion:

"I'm designing classes with optional parameters and I've discovered two types of attributes:

  • Instance attributes like self.name (each object has its own)
  • Class attributes like tricks_known = 0 (shared across all objects)

Explain:

  1. When should I use default parameters in __init__?
  2. What's the memory layout difference between instance and class attributes?
  3. If dog1.tricks_known = 5 creates a new instance attribute, what happens to the class attribute?
  4. Show me a real example where class attributes are useful (not artificial)."

What You'll Learn from AI

Expected AI Response (summary):

  • Default parameters: Make constructors flexible for optional data
  • Instance attributes: Each object has independent copies
  • Class attributes: All objects share one copy
  • Shadowing: Creating instance attribute hides class attribute temporarily
  • Real use case: Configuration values (class) vs instance data (instance)

Convergence Activity

After AI explains, ask:

"Walk me through what happens in memory when I create dog = Dog('Max') in a class that has both species = 'Dog' (class attribute) and self.name = name (instance attribute)."

Deliverable: Write a summary explaining when to use default parameters, class vs instance attributes, and why shadowing is important.


Part 3: Challenge AI with Edge Cases

Your Role: Student teaching AI by testing edge case understanding

Challenge 1: Default Parameters and Mutability

Your prompt to AI:

"I see a lot of code that uses def __init__(self, items: list = []):. But online I see warnings that this is dangerous. Why is using a mutable default parameter (list, dict) problematic? Show me an example where this goes wrong and explain what's happening."

Expected learning: AI will explain that mutable defaults are shared across all instances, causing hidden bugs.

Challenge 2: Class Attributes in AI Agents

Your prompt to AI:

"I'm designing a ChatAgent class. Configuration like model_name = 'gpt-4' could be:

  • A class attribute (all agents use same model)
  • An instance attribute in init (each agent can use different model)

For these scenarios, which should it be:

  1. All agents in production use gpt-4, test agents use gpt-3.5
  2. Each conversation with a user uses a potentially different model
  3. The API key for authentication

Explain your reasoning."

Expected learning: AI will explain design decisions about when to share data.

Challenge 3: Attribute Inheritance Behavior

Your prompt to AI:

"If I create a subclass of Dog:

Loading Python environment...

Does this create an instance attribute on h that shadows Dog.species? Or does it modify the class attribute? Show me how to verify which happened."

Deliverable

Document your three challenges and AI's responses with your analysis of correctness.


Part 4: Build Your Attributes and Constructors Reference

Your Role: Knowledge synthesizer creating design patterns

Create constructor_and_attributes_guide.md:

# Constructors with Defaults and Attributes Guide

## Pattern 1: Basic Constructor with Defaults

**When to use**: Optional parameters that don't need validation

```python
class Person:
def __init__(self, name: str, age: int = 0, city: str = "Unknown"):
self.name = name
self.age = age
self.city = city

# All these work
Person("Alice", 30, "NYC") # All parameters
Person("Bob", 25) # Uses default city
Person("Charlie") # Uses default age and city

Key principle: Required parameters first, optional last


Pattern 2: Default Parameters with Validation

Loading Python environment...

Key principle: Validate before storing


Pattern 3: Distinguishing Instance vs Class Attributes

Instance attributes (use in __init__):

Loading Python environment...

Class attributes (define in class body):

Loading Python environment...

Rule: If data is specific to one object → instance attribute. If data is shared → class attribute.


Pattern 4: Updating Class Attributes

Loading Python environment...


Pattern 5: Detecting Attribute Type

Loading Python environment...


Pattern 6: Avoiding Mutable Default Parameters

Loading Python environment...


Pattern 7: Class Attributes for Configuration

Loading Python environment...


Constructor Pattern Checklist

When designing a constructor, ask:

  1. What parameters are required? (must provide)
  2. What parameters are optional? (have sensible defaults)
  3. Should this be a class attribute? (shared across instances)
  4. Should this be an instance attribute? (specific to each object)
  5. Do I need validation? (check constraints)

Validation with AI

"Review my constructor and attribute design patterns. Are my recommendations about when to use defaults vs required parameters sound? What patterns am I missing?"

Deliverable: Complete guide with all patterns and checklist.


Try With AI

Ready to master constructors with defaults, understand attribute scoping, and avoid mutable default traps?

🔍 Explore Default Parameters:

"Show me a User class with required name parameter and optional email, role (default='user'), created_at (default=now). Create users with: all params, only name, name+email. Explain when defaults help vs when they hide required data. What's the tradeoff?"

🎯 Practice Instance vs Class Attributes:

"Build a BankAccount class where: interest_rate is shared by all accounts (class attribute), but balance is per-account (instance attribute). Create accounts, change interest_rate on the class, show how all accounts see the new rate. When should attributes be shared vs independent?"

🧪 Test Mutable Default Danger:

"Create a Team class with members parameter defaulting to []. Add members to team1. Create team2 without members. What's in team2.members? Why? Show me the fix (members=None, then self.members = members or []). Explain the Python default parameter gotcha."

🚀 Apply to Configuration:

"I'm building a database connection class. It needs: required host/database, optional port (default 5432), user (default 'admin'), connection_pool settings (dict). Design the init with appropriate defaults. Which params must be provided vs can have sensible defaults?"