Web Analytics

Classes & Objects

Intermediate ~30 min read

Object-Oriented Programming (OOP) lets you model real-world things as objects with attributes (data) and methods (behavior). A class is a blueprint, and objects are instances created from that blueprint. Almost everything in Python is an object - strings, lists, functions - and now you'll learn to create your own!

Class Basics

Define a class with the class keyword. Classes are blueprints for creating objects. When you create an object from a class, you get an instance with its own data. Each instance is independent - changing one doesn't affect others.

Output
Click Run to execute your code
Key Concepts:
class ClassName: - Define a class (PascalCase naming)
obj = ClassName() - Create an instance (object)
type(obj) - Check object's type
isinstance(obj, Class) - Check if object is instance of class

Remember: A class is a blueprint; objects are instances built from it.

The __init__ Method

The __init__ method (constructor) initializes new objects. It runs automatically when you create an instance. The first parameter is always self, which refers to the instance being created. Use it to set up instance attributes with initial values.

Output
Click Run to execute your code
About self:
- self is the instance being created or operated on
- It's passed automatically - you don't provide it when calling
- Use self.attribute = value to set instance attributes
- __init__ doesn't return anything (always returns None)

Note: self is just a convention - you could use any name, but don't!

Instance vs Class Attributes

Instance attributes are unique to each object - set them in __init__ using self.attr = value. Class attributes are shared by all instances - define them directly in the class body. Be careful: assigning to an instance can shadow class attributes!

Output
Click Run to execute your code
Mutable Class Attribute Trap!
Never use mutable defaults (lists, dicts) as class attributes! They're shared by all instances.

class Bad: items = [] - All instances share the same list!
class Good: def __init__(self): self.items = [] - Each gets its own list.

Working with Objects

Objects are reference types - variables hold references to objects, not the objects themselves. This means passing objects to functions passes the reference, allowing modification of the original. Use is to check identity (same object) and == for equality (same value).

Output
Click Run to execute your code
Object Operations:
obj.attr - Access attribute
obj.attr = value - Set attribute
hasattr(obj, 'attr') - Check if attribute exists
getattr(obj, 'attr', default) - Get attribute with default
obj.__dict__ - Dictionary of instance attributes
id(obj) - Unique object identifier (memory address)

Common Mistakes

1. Forgetting self in __init__

# Wrong - name becomes local variable, not attribute!
class Person:
    def __init__(self, name):
        name = name  # Useless! Doesn't save to instance

# Correct - use self to save to instance
class Person:
    def __init__(self, name):
        self.name = name  # Now it's an instance attribute

2. Mutable default in class body

# Wrong - all instances share the same list!
class Team:
    members = []  # Class attribute - shared!

t1 = Team()
t2 = Team()
t1.members.append("Alice")
print(t2.members)  # ['Alice'] - OOPS!

# Correct - create list in __init__
class Team:
    def __init__(self):
        self.members = []  # Instance attribute - unique

3. Confusing class name with variable

# Wrong - calling class like function without saving result
class User:
    def __init__(self, name):
        self.name = name

User("Alice")  # Creates object but throws it away!

# Correct - save the instance to a variable
user = User("Alice")
print(user.name)  # Now we can use it

4. Shadowing class attributes

class Counter:
    count = 0  # Class attribute

c1 = Counter()
c2 = Counter()

# Wrong - this creates an INSTANCE attribute on c1!
c1.count = 5
print(Counter.count)  # Still 0!
print(c2.count)       # Still 0!

# Correct - modify class attribute through class name
Counter.count = 5
print(c1.count)  # 5 (reads class attr)
print(c2.count)  # 5

5. Returning from __init__

# Wrong - __init__ should not return anything!
class Builder:
    def __init__(self, name):
        self.name = name
        return self  # Error or ignored!

# Correct - just set attributes, no return
class Builder:
    def __init__(self, name):
        self.name = name
        # Implicitly returns None

Exercise: Bank Account Class

Task: Create a BankAccount class with proper initialization.

Requirements:

  • Initialize with owner name and optional starting balance (default 0)
  • Add a class attribute to track total accounts created
  • Validate that balance is not negative in __init__
  • Create a method to display account info
Output
Click Run to execute your code
Show Solution
class BankAccount:
    """A simple bank account class."""
    # Class attribute - tracks all accounts
    total_accounts = 0

    def __init__(self, owner, balance=0):
        """Initialize account with owner and optional balance."""
        if not owner:
            raise ValueError("Owner name is required")
        if balance < 0:
            raise ValueError("Initial balance cannot be negative")

        self.owner = owner
        self.balance = balance
        self.account_number = BankAccount.total_accounts + 1000

        # Increment class counter
        BankAccount.total_accounts += 1

    def display(self):
        """Display account information."""
        return f"Account #{self.account_number}: {self.owner}, Balance: ${self.balance:.2f}"


# Test the class
acc1 = BankAccount("Alice", 1000)
acc2 = BankAccount("Bob", 500)
acc3 = BankAccount("Charlie")  # Uses default balance

print("=== Bank Accounts ===")
print(acc1.display())
print(acc2.display())
print(acc3.display())
print(f"\nTotal accounts created: {BankAccount.total_accounts}")

# Test validation
try:
    bad = BankAccount("", 100)
except ValueError as e:
    print(f"\nValidation error: {e}")

Summary

  • class: class ClassName: defines a blueprint for objects
  • Object: An instance created from a class: obj = ClassName()
  • __init__: Constructor method that initializes new instances
  • self: First parameter, refers to the instance being created/used
  • Instance attributes: Unique per object, set with self.attr
  • Class attributes: Shared by all instances, defined in class body
  • Identity: is checks same object, == checks equality
  • Avoid: Mutable class attributes, forgetting self, returning from __init__

What's Next?

Now that you can create classes with attributes, it's time to add behavior! Next, we'll learn about methods - functions that belong to classes. You'll learn about instance methods, class methods, static methods, and how to make your objects do things!