You're tracking users in your app. Each user has a name, email, and login method. Instead of managing separate dictionaries, a class bundles the data and behavior into one reusable blueprint - cleaner and more maintainable.

Basic class definition

Create a class with attributes and methods.

basic_class.py
Replay: real traced execution (multi-file project)
# Basic Class Definition

print("=== Basic Class ===\n")

# Define a simple class
class Dog:
    pass  # Empty class for now

# Create an object (instance)
my_dog = Dog()
print(f"Created: {my_dog}")
print(f"Type: {type(my_dog)}")

print("\n=== Class with Attribute ===")

class Cat:
    species = "Felis catus"  # Class attribute

# Create cats
cat1 = Cat()
cat2 = Cat()

print(f"Cat 1 species: {cat1.species}")
print(f"Cat 2 species: {cat2.species}")

# Both share the same class attribute
print(f"Same attribute? {cat1.species is cat2.species}")

print("\n=== Adding Instance Attributes ===")

# Add attributes to specific instance
cat1.name = "Whiskers"
cat2.name = "Luna"

print(f"Cat 1 name: {cat1.name}")
print(f"Cat 2 name: {cat2.name}")

print("\n=== Class vs Object ===")

print("""
Class = Blueprint/Template
  - Defines structure
  - Like a cookie cutter

Object = Instance/Reality
  - Actual data
  - Like actual cookies
""")

# Multiple objects from same class
dog1 = Dog()
dog2 = Dog()
dog3 = Dog()

print(f"dog1 is dog2? {dog1 is dog2}")  # False - different objects
print(f"All are Dogs? {type(dog1) == type(dog2) == Dog}")  # True
  1. my_dog ← ⟨Dog A⟩, species ← (empty), cat1 ← ⟨Cat B⟩, cat2 ← ⟨Cat C⟩

    3print("=== Basic Class ===\n")45# Define a simple class  #?defclass6class Dog:  #?classdef7    pass  # Empty class for now  #?pass89# Create an object (instance)  #?createobj10my_dog→ ⟨Dog A⟩ = Dog()  #?instantiate11print(f"Created: {my_dog⟨Dog A⟩}")12print(f"Type: {type(my_dog⟨Dog A⟩)}")1314print("\n=== Class with Attribute ===")1516class Cat:  #?catwithattr17    species→ (empty) = "Felis catus"  # Class attribute  #?classattr1819# Create cats20cat1→ ⟨Cat B⟩ = Cat()21cat2→ ⟨Cat C⟩ = Cat()2223print(f"Cat 1 species: {cat1.speciesFelis catus}")24print(f"Cat 2 species: {cat2.speciesFelis catus}")2526# Both share the same class attribute  #?shared27print(f"Same attribute? {cat1.speciesFelis catus is cat2.speciesFelis catus}")2829print("\n=== Adding Instance Attributes ===")3031# Add attributes to specific instance  #?instanceattr32cat1.name→ Whiskers = "Whiskers"  #?addattr33cat2.name→ Luna = "Luna"3435print(f"Cat 1 name: {cat1.nameWhiskers}")36print(f"Cat 2 name: {cat2.nameLuna}")3738print("\n=== Class vs Object ===")3940print("""41Class = Blueprint/Template42  - Defines structure43  - Like a cookie cutter4445Object = Instance/Reality46  - Actual data47  - Like actual cookies48""")4950# Multiple objects from same class  #?multiple51dog1→ ⟨Dog D⟩ = Dog()52dog2→ ⟨Dog E⟩ = Dog()53dog3→ ⟨Dog F⟩ = Dog()5455print(f"dog1 is dog2? {dog1⟨Dog D⟩ is dog2⟨Dog E⟩}")  # False - different objects  #?different56print(f"All are Dogs? {type(dog1⟨Dog D⟩) == type(dog2⟨Dog E⟩) == Dog<class '__main__.Dog'>}")  # True57#@help defclass
    output=== Basic Class ===
    Created: ⟨Dog A⟩
    Type: <class '__main__.Dog'>
    
    === Class with Attribute ===
    Cat 1 species: Felis catus
    Cat 2 species: Felis catus
    Same attribute? True
    
    === Adding Instance Attributes ===
    Cat 1 name: Whiskers
    Cat 2 name: Luna
    
    === Class vs Object ===
    
    Class = Blueprint/Template
      - Defines structure
      - Like a cookie cutter
    
    Object = Instance/Reality
      - Actual data
      - Like actual cookies
    dog1 is dog2? False
    All are Dogs? True

class Name: defines a blueprint. Methods are functions inside the class.

class Blueprint for objects. Defines attributes (data) and methods (behavior).

Instance attributes

Each object has its own copy of attributes.

example
attributes.py
Replay: real traced execution (multi-file project)
# Instance Attributes with __init__

print("=== The __init__ Method ===\n")

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(f"Creating dog: {name}, age {age}")

# Create dogs
buddy = Dog("Buddy", 3)
max_dog = Dog("Max", 5)

print(f"\n{buddy.name} is {buddy.age} years old")
print(f"{max_dog.name} is {max_dog.age} years old")

print("\n=== Default Values ===")

class Cat:
    def __init__(self, name, color="orange"):
        self.name = name
        self.color = color

# With and without default
garfield = Cat("Garfield")  # Uses default color
whiskers = Cat("Whiskers", "gray")

print(f"{garfield.name} is {garfield.color}")
print(f"{whiskers.name} is {whiskers.color}")

print("\n=== Multiple Initialization Patterns ===")

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

    def display(self):
        print(f"{self.name}, {self.age}, from {self.city}")

# Various ways to create
p1 = Person("Alice")
p2 = Person("Bob", 25)
p3 = Person("Carol", 30, "NYC")
p4 = Person("Dave", city="LA")

p1.display()
p2.display()
p3.display()
p4.display()

print("\n=== Computed Attributes ===")

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.area = width * height
        self.perimeter = 2 * (width + height)

rect = Rectangle(5, 3)
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
# Instance Attributes with __init__

print("=== The __init__ Method ===\n")

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(f"Creating dog: {name}, age {age}")

# Create dogs
buddy = Dog("Rex", 2)
max_dog = Dog("Max", 5)

print(f"\n{buddy.name} is {buddy.age} years old")
print(f"{max_dog.name} is {max_dog.age} years old")

print("\n=== Default Values ===")

class Cat:
    def __init__(self, name, color="orange"):
        self.name = name
        self.color = color

# With and without default
garfield = Cat("Garfield")  # Uses default color
whiskers = Cat("Whiskers", "gray")

print(f"{garfield.name} is {garfield.color}")
print(f"{whiskers.name} is {whiskers.color}")

print("\n=== Multiple Initialization Patterns ===")

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

    def display(self):
        print(f"{self.name}, {self.age}, from {self.city}")

# Various ways to create
p1 = Person("Alice")
p2 = Person("Bob", 25)
p3 = Person("Carol", 30, "NYC")
p4 = Person("Dave", city="LA")

p1.display()
p2.display()
p3.display()
p4.display()

print("\n=== Computed Attributes ===")

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.area = width * height
        self.perimeter = 2 * (width + height)

rect = Rectangle(5, 3)
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
# Instance Attributes with __init__

print("=== The __init__ Method ===\n")

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(f"Creating dog: {name}, age {age}")

# Create dogs
buddy = Dog("Luna", 7)
max_dog = Dog("Max", 5)

print(f"\n{buddy.name} is {buddy.age} years old")
print(f"{max_dog.name} is {max_dog.age} years old")

print("\n=== Default Values ===")

class Cat:
    def __init__(self, name, color="orange"):
        self.name = name
        self.color = color

# With and without default
garfield = Cat("Garfield")  # Uses default color
whiskers = Cat("Whiskers", "gray")

print(f"{garfield.name} is {garfield.color}")
print(f"{whiskers.name} is {whiskers.color}")

print("\n=== Multiple Initialization Patterns ===")

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

    def display(self):
        print(f"{self.name}, {self.age}, from {self.city}")

# Various ways to create
p1 = Person("Alice")
p2 = Person("Bob", 25)
p3 = Person("Carol", 30, "NYC")
p4 = Person("Dave", city="LA")

p1.display()
p2.display()
p3.display()
p4.display()

print("\n=== Computed Attributes ===")

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.area = width * height
        self.perimeter = 2 * (width + height)

rect = Rectangle(5, 3)
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
# Instance Attributes with __init__

print("=== The __init__ Method ===\n")

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(f"Creating dog: {name}, age {age}")

# Create dogs
buddy = Dog("Buddy", 3)
max_dog = Dog("Max", 5)

print(f"\n{buddy.name} is {buddy.age} years old")
print(f"{max_dog.name} is {max_dog.age} years old")

print("\n=== Default Values ===")

class Cat:
    def __init__(self, name, color="orange"):
        self.name = name
        self.color = color

# With and without default
garfield = Cat("Garfield")  # Uses default color
whiskers = Cat("Whiskers", "gray")

print(f"{garfield.name} is {garfield.color}")
print(f"{whiskers.name} is {whiskers.color}")

print("\n=== Multiple Initialization Patterns ===")

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

    def display(self):
        print(f"{self.name}, {self.age}, from {self.city}")

# Various ways to create
p1 = Person("Alice")
p2 = Person("Bob", 25)
p3 = Person("Carol", 30, "NYC")
p4 = Person("Dave", city="LA")

p1.display()
p2.display()
p3.display()
p4.display()

print("\n=== Computed Attributes ===")

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.area = width * height
        self.perimeter = 2 * (width + height)

rect = Rectangle(4, 4)
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
# Instance Attributes with __init__

print("=== The __init__ Method ===\n")

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print(f"Creating dog: {name}, age {age}")

# Create dogs
buddy = Dog("Buddy", 3)
max_dog = Dog("Max", 5)

print(f"\n{buddy.name} is {buddy.age} years old")
print(f"{max_dog.name} is {max_dog.age} years old")

print("\n=== Default Values ===")

class Cat:
    def __init__(self, name, color="orange"):
        self.name = name
        self.color = color

# With and without default
garfield = Cat("Garfield")  # Uses default color
whiskers = Cat("Whiskers", "gray")

print(f"{garfield.name} is {garfield.color}")
print(f"{whiskers.name} is {whiskers.color}")

print("\n=== Multiple Initialization Patterns ===")

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

    def display(self):
        print(f"{self.name}, {self.age}, from {self.city}")

# Various ways to create
p1 = Person("Alice")
p2 = Person("Bob", 25)
p3 = Person("Carol", 30, "NYC")
p4 = Person("Dave", city="LA")

p1.display()
p2.display()
p3.display()
p4.display()

print("\n=== Computed Attributes ===")

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.area = width * height
        self.perimeter = 2 * (width + height)

rect = Rectangle(8, 2)
print(f"Rectangle {rect.width}x{rect.height}")
print(f"Area: {rect.area}")
print(f"Perimeter: {rect.perimeter}")
  1. print("=== The __init__ Method === ")

    3print("=== The __init__ Method ===\n")45class Dog:6    def __init__(self, name, age):  #?init7        self.name = name  #?selfattr8        self.age = age9        print(f"Creating dog: {name}, age {age}")1011# Create dogs  #?createwithargs12buddy = Dog("Buddy", 3)  #?passargs13#@buddy=Dog("Rex", 2), Dog("Luna", 7)
    output=== The __init__ Method ===
  2. self.name ← Buddy, self.age ← 3

    pass 1 of 2
    5class Dog:6    def __init__(self⟨Dog A⟩, nameBuddy, age3):  #?init7        self.name→ Buddy = nameBuddy  #?selfattr8        self.age→ 3 = age39        print(f"Creating dog: {nameBuddy}, age {age3}")
    outputCreating dog: Buddy, age 3
  3. buddy ← ⟨Dog A⟩

    11# Create dogs  #?createwithargs12buddy→ ⟨Dog A⟩ = Dog("Buddy", 3)  #?passargs13#@buddy=Dog("Rex", 2), Dog("Luna", 7)14max_dog = Dog("Max", 5)
  4. self.name ← Max, self.age ← 5

    pass 2 of 2
    5class Dog:6    def __init__(self⟨Dog B⟩, nameMax, age5):  #?init7        self.name→ Max = nameMax  #?selfattr8        self.age→ 5 = age59        print(f"Creating dog: {nameMax}, age {age5}")
    outputCreating dog: Max, age 5
  5. max_dog ← ⟨Dog B⟩

    13#@buddy=Dog("Rex", 2), Dog("Luna", 7)14max_dog→ ⟨Dog B⟩ = Dog("Max", 5)1516print(f"\n{buddy.nameBuddy} is {buddy.age3} years old")17print(f"{max_dog.nameMax} is {max_dog.age5} years old")1819print("\n=== Default Values ===")2021class Cat:22    def __init__(self, name, color="orange"):  #?default23        self.name = name24        self.color = color2526# With and without default  #?usedefault27garfield = Cat("Garfield")  # Uses default color28whiskers = Cat("Whiskers", "gray")
    output
    Buddy is 3 years old
    Max is 5 years old
    
    === Default Values ===
  6. self.name ← Garfield, self.color ← orange

    pass 1 of 2
    21class Cat:22    def __init__(self⟨Cat C⟩, nameGarfield, colororange="orange"):  #?default23        self.name→ Garfield = nameGarfield24        self.color→ orange = colororange
  7. garfield ← ⟨Cat C⟩

    26# With and without default  #?usedefault27garfield→ ⟨Cat C⟩ = Cat("Garfield")  # Uses default color28whiskers = Cat("Whiskers", "gray")
  8. self.name ← Whiskers, self.color ← gray

    pass 2 of 2
    21class Cat:22    def __init__(self⟨Cat D⟩, nameWhiskers, colorgray="orange"):  #?default23        self.name→ Whiskers = nameWhiskers24        self.color→ gray = colorgray
  9. whiskers ← ⟨Cat D⟩

    27garfield = Cat("Garfield")  # Uses default color28whiskers→ ⟨Cat D⟩ = Cat("Whiskers", "gray")2930print(f"{garfield.nameGarfield} is {garfield.colororange}")31print(f"{whiskers.nameWhiskers} is {whiskers.colorgray}")3233print("\n=== Multiple Initialization Patterns ===")3435class Person:36    def __init__(self, name, age=0, city="Unknown"):  #?multidefault37        self.name = name38        self.age = age39        self.city = city40    41    def display(self):42        print(f"{self.name}, {self.age}, from {self.city}")4344# Various ways to create  #?createvarious45p1 = Person("Alice")46p2 = Person("Bob", 25)
    outputGarfield is orange
    Whiskers is gray
    
    === Multiple Initialization Patterns ===
  10. self.name ← Alice, self.age ← 0, self.city ← Unknown

    pass 1 of 4
    35class Person:36    def __init__(self⟨Person E⟩, nameAlice, age0=0, cityUnknown="Unknown"):  #?multidefault37        self.name→ Alice = nameAlice38        self.age→ 0 = age039        self.city→ Unknown = cityUnknown
    All 4 passes — pass 1 is the card above
    passselfnameagecityself.nameself.ageself.city
    1⟨Person E⟩Alice0UnknownAlice0Unknown
    2⟨Person F⟩Bob25UnknownBob25Unknown
    3⟨Person G⟩Carol30NYCCarol30NYC
    4⟨Person H⟩Dave0LADave0LA
  11. p1 ← ⟨Person E⟩

    44# Various ways to create  #?createvarious45p1→ ⟨Person E⟩ = Person("Alice")46p2 = Person("Bob", 25)47p3 = Person("Carol", 30, "NYC")
  12. p2 ← ⟨Person F⟩

    45p1 = Person("Alice")46p2→ ⟨Person F⟩ = Person("Bob", 25)47p3 = Person("Carol", 30, "NYC")48p4 = Person("Dave", city="LA")  #?namedarg
  13. p3 ← ⟨Person G⟩

    46p2 = Person("Bob", 25)47p3→ ⟨Person G⟩ = Person("Carol", 30, "NYC")48p4 = Person("Dave", city="LA")  #?namedarg
  14. p4 ← ⟨Person H⟩

    47p3 = Person("Carol", 30, "NYC")48p4→ ⟨Person H⟩ = Person("Dave", city="LA")  #?namedarg4950p1⟨Person E⟩.display()51p2.display()
  15. def display(self):

    pass 1 of 4
    41def display(self⟨Person E⟩):42    print(f"{self.nameAlice}, {self.age0}, from {self.cityUnknown}")
    outputAlice, 0, from Unknown
    All 4 passes — pass 1 is the card above
    passselfself.nameself.ageself.city
    1⟨Person E⟩Alice0Unknown
    2⟨Person F⟩Bob25Unknown
    3⟨Person G⟩Carol30NYC
    4⟨Person H⟩Dave0LA
  16. p1.display()

    50p1⟨Person E⟩.display()51p2⟨Person F⟩.display()52p3.display()
  17. p2.display()

    50p1.display()51p2⟨Person F⟩.display()52p3⟨Person G⟩.display()53p4.display()
  18. p3.display()

    51p2.display()52p3⟨Person G⟩.display()53p4⟨Person H⟩.display()
  19. p4.display()

    52p3.display()53p4⟨Person H⟩.display()5455print("\n=== Computed Attributes ===")5657class Rectangle:58    def __init__(self, width, height):59        self.width = width60        self.height = height61        self.area = width * height  #?computed62        self.perimeter = 2 * (width + height)6364rect = Rectangle(5, 3)  #@rect=Rectangle(4, 4), Rectangle(8, 2)65print(f"Rectangle {rect.width}x{rect.height}")
    output
    === Computed Attributes ===
  20. self.width ← 5, self.height ← 3, self.area ← 15, self.perimeter ← 16

    57class Rectangle:58    def __init__(self⟨Rectangle I⟩, width5, height3):59        self.width→ 5 = width560        self.height→ 3 = height361        self.area→ 15 = width5 * height3  #?computed62        self.perimeter→ 16 = 2 * (width5 + height3)
  21. rect ← ⟨Rectangle I⟩

    64rect→ ⟨Rectangle I⟩ = Rectangle(5, 3)  #@rect=Rectangle(4, 4), Rectangle(8, 2)65print(f"Rectangle {rect.width5}x{rect.height3}")66print(f"Area: {rect.area15}")67print(f"Perimeter: {rect.perimeter16}")68#@help init
    outputRectangle 5x3
    Area: 15
    Perimeter: 16
  1. print("=== The __init__ Method === ")

    3print("=== The __init__ Method ===\n")45class Dog:6    def __init__(self, name, age):7        self.name = name8        self.age = age9        print(f"Creating dog: {name}, age {age}")1011# Create dogs12buddy = Dog("Rex", 2)13max_dog = Dog("Max", 5)
    output=== The __init__ Method ===
  2. self.name ← Rex, self.age ← 2

    pass 1 of 2
    5class Dog:6    def __init__(self⟨Dog A⟩, nameRex, age2):7        self.name→ Rex = nameRex8        self.age→ 2 = age29        print(f"Creating dog: {nameRex}, age {age2}")
    outputCreating dog: Rex, age 2
  3. buddy ← ⟨Dog A⟩

    11# Create dogs12buddy→ ⟨Dog A⟩ = Dog("Rex", 2)13max_dog = Dog("Max", 5)
  4. self.name ← Max, self.age ← 5

    pass 2 of 2
    5class Dog:6    def __init__(self⟨Dog B⟩, nameMax, age5):7        self.name→ Max = nameMax8        self.age→ 5 = age59        print(f"Creating dog: {nameMax}, age {age5}")
    outputCreating dog: Max, age 5
  5. max_dog ← ⟨Dog B⟩

    12buddy = Dog("Rex", 2)13max_dog→ ⟨Dog B⟩ = Dog("Max", 5)1415print(f"\n{buddy.nameRex} is {buddy.age2} years old")16print(f"{max_dog.nameMax} is {max_dog.age5} years old")1718print("\n=== Default Values ===")1920class Cat:21    def __init__(self, name, color="orange"):22        self.name = name23        self.color = color2425# With and without default26garfield = Cat("Garfield")  # Uses default color27whiskers = Cat("Whiskers", "gray")
    output
    Rex is 2 years old
    Max is 5 years old
    
    === Default Values ===
  6. self.name ← Garfield, self.color ← orange

    pass 1 of 2
    20class Cat:21    def __init__(self⟨Cat C⟩, nameGarfield, colororange="orange"):22        self.name→ Garfield = nameGarfield23        self.color→ orange = colororange
  7. garfield ← ⟨Cat C⟩

    25# With and without default26garfield→ ⟨Cat C⟩ = Cat("Garfield")  # Uses default color27whiskers = Cat("Whiskers", "gray")
  8. self.name ← Whiskers, self.color ← gray

    pass 2 of 2
    20class Cat:21    def __init__(self⟨Cat D⟩, nameWhiskers, colorgray="orange"):22        self.name→ Whiskers = nameWhiskers23        self.color→ gray = colorgray
  9. whiskers ← ⟨Cat D⟩

    26garfield = Cat("Garfield")  # Uses default color27whiskers→ ⟨Cat D⟩ = Cat("Whiskers", "gray")2829print(f"{garfield.nameGarfield} is {garfield.colororange}")30print(f"{whiskers.nameWhiskers} is {whiskers.colorgray}")3132print("\n=== Multiple Initialization Patterns ===")3334class Person:35    def __init__(self, name, age=0, city="Unknown"):36        self.name = name37        self.age = age38        self.city = city39    40    def display(self):41        print(f"{self.name}, {self.age}, from {self.city}")4243# Various ways to create44p1 = Person("Alice")45p2 = Person("Bob", 25)
    outputGarfield is orange
    Whiskers is gray
    
    === Multiple Initialization Patterns ===
  10. self.name ← Alice, self.age ← 0, self.city ← Unknown

    pass 1 of 4
    34class Person:35    def __init__(self⟨Person E⟩, nameAlice, age0=0, cityUnknown="Unknown"):36        self.name→ Alice = nameAlice37        self.age→ 0 = age038        self.city→ Unknown = cityUnknown
    All 4 passes — pass 1 is the card above
    passselfnameagecityself.nameself.ageself.city
    1⟨Person E⟩Alice0UnknownAlice0Unknown
    2⟨Person F⟩Bob25UnknownBob25Unknown
    3⟨Person G⟩Carol30NYCCarol30NYC
    4⟨Person H⟩Dave0LADave0LA
  11. p1 ← ⟨Person E⟩

    43# Various ways to create44p1→ ⟨Person E⟩ = Person("Alice")45p2 = Person("Bob", 25)46p3 = Person("Carol", 30, "NYC")
  12. p2 ← ⟨Person F⟩

    44p1 = Person("Alice")45p2→ ⟨Person F⟩ = Person("Bob", 25)46p3 = Person("Carol", 30, "NYC")47p4 = Person("Dave", city="LA")
  13. p3 ← ⟨Person G⟩

    45p2 = Person("Bob", 25)46p3→ ⟨Person G⟩ = Person("Carol", 30, "NYC")47p4 = Person("Dave", city="LA")
  14. p4 ← ⟨Person H⟩

    46p3 = Person("Carol", 30, "NYC")47p4→ ⟨Person H⟩ = Person("Dave", city="LA")4849p1⟨Person E⟩.display()50p2.display()
  15. def display(self):

    pass 1 of 4
    40def display(self⟨Person E⟩):41    print(f"{self.nameAlice}, {self.age0}, from {self.cityUnknown}")
    outputAlice, 0, from Unknown
    All 4 passes — pass 1 is the card above
    passselfself.nameself.ageself.city
    1⟨Person E⟩Alice0Unknown
    2⟨Person F⟩Bob25Unknown
    3⟨Person G⟩Carol30NYC
    4⟨Person H⟩Dave0LA
  16. p1.display()

    49p1⟨Person E⟩.display()50p2⟨Person F⟩.display()51p3.display()
  17. p2.display()

    49p1.display()50p2⟨Person F⟩.display()51p3⟨Person G⟩.display()52p4.display()
  18. p3.display()

    50p2.display()51p3⟨Person G⟩.display()52p4⟨Person H⟩.display()
  19. p4.display()

    51p3.display()52p4⟨Person H⟩.display()5354print("\n=== Computed Attributes ===")5556class Rectangle:57    def __init__(self, width, height):58        self.width = width59        self.height = height60        self.area = width * height61        self.perimeter = 2 * (width + height)6263rect = Rectangle(5, 3)64print(f"Rectangle {rect.width}x{rect.height}")
    output
    === Computed Attributes ===
  20. self.width ← 5, self.height ← 3, self.area ← 15, self.perimeter ← 16

    56class Rectangle:57    def __init__(self⟨Rectangle I⟩, width5, height3):58        self.width→ 5 = width559        self.height→ 3 = height360        self.area→ 15 = width5 * height361        self.perimeter→ 16 = 2 * (width5 + height3)
  21. rect ← ⟨Rectangle I⟩

    63rect→ ⟨Rectangle I⟩ = Rectangle(5, 3)64print(f"Rectangle {rect.width5}x{rect.height3}")65print(f"Area: {rect.area15}")66print(f"Perimeter: {rect.perimeter16}")
    outputRectangle 5x3
    Area: 15
    Perimeter: 16
  1. print("=== The __init__ Method === ")

    3print("=== The __init__ Method ===\n")45class Dog:6    def __init__(self, name, age):7        self.name = name8        self.age = age9        print(f"Creating dog: {name}, age {age}")1011# Create dogs12buddy = Dog("Luna", 7)13max_dog = Dog("Max", 5)
    output=== The __init__ Method ===
  2. self.name ← Luna, self.age ← 7

    pass 1 of 2
    5class Dog:6    def __init__(self⟨Dog A⟩, nameLuna, age7):7        self.name→ Luna = nameLuna8        self.age→ 7 = age79        print(f"Creating dog: {nameLuna}, age {age7}")
    outputCreating dog: Luna, age 7
  3. buddy ← ⟨Dog A⟩

    11# Create dogs12buddy→ ⟨Dog A⟩ = Dog("Luna", 7)13max_dog = Dog("Max", 5)
  4. self.name ← Max, self.age ← 5

    pass 2 of 2
    5class Dog:6    def __init__(self⟨Dog B⟩, nameMax, age5):7        self.name→ Max = nameMax8        self.age→ 5 = age59        print(f"Creating dog: {nameMax}, age {age5}")
    outputCreating dog: Max, age 5
  5. max_dog ← ⟨Dog B⟩

    12buddy = Dog("Luna", 7)13max_dog→ ⟨Dog B⟩ = Dog("Max", 5)1415print(f"\n{buddy.nameLuna} is {buddy.age7} years old")16print(f"{max_dog.nameMax} is {max_dog.age5} years old")1718print("\n=== Default Values ===")1920class Cat:21    def __init__(self, name, color="orange"):22        self.name = name23        self.color = color2425# With and without default26garfield = Cat("Garfield")  # Uses default color27whiskers = Cat("Whiskers", "gray")
    output
    Luna is 7 years old
    Max is 5 years old
    
    === Default Values ===
  6. self.name ← Garfield, self.color ← orange

    pass 1 of 2
    20class Cat:21    def __init__(self⟨Cat C⟩, nameGarfield, colororange="orange"):22        self.name→ Garfield = nameGarfield23        self.color→ orange = colororange
  7. garfield ← ⟨Cat C⟩

    25# With and without default26garfield→ ⟨Cat C⟩ = Cat("Garfield")  # Uses default color27whiskers = Cat("Whiskers", "gray")
  8. self.name ← Whiskers, self.color ← gray

    pass 2 of 2
    20class Cat:21    def __init__(self⟨Cat D⟩, nameWhiskers, colorgray="orange"):22        self.name→ Whiskers = nameWhiskers23        self.color→ gray = colorgray
  9. whiskers ← ⟨Cat D⟩

    26garfield = Cat("Garfield")  # Uses default color27whiskers→ ⟨Cat D⟩ = Cat("Whiskers", "gray")2829print(f"{garfield.nameGarfield} is {garfield.colororange}")30print(f"{whiskers.nameWhiskers} is {whiskers.colorgray}")3132print("\n=== Multiple Initialization Patterns ===")3334class Person:35    def __init__(self, name, age=0, city="Unknown"):36        self.name = name37        self.age = age38        self.city = city39    40    def display(self):41        print(f"{self.name}, {self.age}, from {self.city}")4243# Various ways to create44p1 = Person("Alice")45p2 = Person("Bob", 25)
    outputGarfield is orange
    Whiskers is gray
    
    === Multiple Initialization Patterns ===
  10. self.name ← Alice, self.age ← 0, self.city ← Unknown

    pass 1 of 4
    34class Person:35    def __init__(self⟨Person E⟩, nameAlice, age0=0, cityUnknown="Unknown"):36        self.name→ Alice = nameAlice37        self.age→ 0 = age038        self.city→ Unknown = cityUnknown
    All 4 passes — pass 1 is the card above
    passselfnameagecityself.nameself.ageself.city
    1⟨Person E⟩Alice0UnknownAlice0Unknown
    2⟨Person F⟩Bob25UnknownBob25Unknown
    3⟨Person G⟩Carol30NYCCarol30NYC
    4⟨Person H⟩Dave0LADave0LA
  11. p1 ← ⟨Person E⟩

    43# Various ways to create44p1→ ⟨Person E⟩ = Person("Alice")45p2 = Person("Bob", 25)46p3 = Person("Carol", 30, "NYC")
  12. p2 ← ⟨Person F⟩

    44p1 = Person("Alice")45p2→ ⟨Person F⟩ = Person("Bob", 25)46p3 = Person("Carol", 30, "NYC")47p4 = Person("Dave", city="LA")
  13. p3 ← ⟨Person G⟩

    45p2 = Person("Bob", 25)46p3→ ⟨Person G⟩ = Person("Carol", 30, "NYC")47p4 = Person("Dave", city="LA")
  14. p4 ← ⟨Person H⟩

    46p3 = Person("Carol", 30, "NYC")47p4→ ⟨Person H⟩ = Person("Dave", city="LA")4849p1⟨Person E⟩.display()50p2.display()
  15. def display(self):

    pass 1 of 4
    40def display(self⟨Person E⟩):41    print(f"{self.nameAlice}, {self.age0}, from {self.cityUnknown}")
    outputAlice, 0, from Unknown
    All 4 passes — pass 1 is the card above
    passselfself.nameself.ageself.city
    1⟨Person E⟩Alice0Unknown
    2⟨Person F⟩Bob25Unknown
    3⟨Person G⟩Carol30NYC
    4⟨Person H⟩Dave0LA
  16. p1.display()

    49p1⟨Person E⟩.display()50p2⟨Person F⟩.display()51p3.display()
  17. p2.display()

    49p1.display()50p2⟨Person F⟩.display()51p3⟨Person G⟩.display()52p4.display()
  18. p3.display()

    50p2.display()51p3⟨Person G⟩.display()52p4⟨Person H⟩.display()
  19. p4.display()

    51p3.display()52p4⟨Person H⟩.display()5354print("\n=== Computed Attributes ===")5556class Rectangle:57    def __init__(self, width, height):58        self.width = width59        self.height = height60        self.area = width * height61        self.perimeter = 2 * (width + height)6263rect = Rectangle(5, 3)64print(f"Rectangle {rect.width}x{rect.height}")
    output
    === Computed Attributes ===
  20. self.width ← 5, self.height ← 3, self.area ← 15, self.perimeter ← 16

    56class Rectangle:57    def __init__(self⟨Rectangle I⟩, width5, height3):58        self.width→ 5 = width559        self.height→ 3 = height360        self.area→ 15 = width5 * height361        self.perimeter→ 16 = 2 * (width5 + height3)
  21. rect ← ⟨Rectangle I⟩

    63rect→ ⟨Rectangle I⟩ = Rectangle(5, 3)64print(f"Rectangle {rect.width5}x{rect.height3}")65print(f"Area: {rect.area15}")66print(f"Perimeter: {rect.perimeter16}")
    outputRectangle 5x3
    Area: 15
    Perimeter: 16
  1. print("=== The __init__ Method === ")

    3print("=== The __init__ Method ===\n")45class Dog:6    def __init__(self, name, age):7        self.name = name8        self.age = age9        print(f"Creating dog: {name}, age {age}")1011# Create dogs12buddy = Dog("Buddy", 3)13max_dog = Dog("Max", 5)
    output=== The __init__ Method ===
  2. self.name ← Buddy, self.age ← 3

    pass 1 of 2
    5class Dog:6    def __init__(self⟨Dog A⟩, nameBuddy, age3):7        self.name→ Buddy = nameBuddy8        self.age→ 3 = age39        print(f"Creating dog: {nameBuddy}, age {age3}")
    outputCreating dog: Buddy, age 3
  3. buddy ← ⟨Dog A⟩

    11# Create dogs12buddy→ ⟨Dog A⟩ = Dog("Buddy", 3)13max_dog = Dog("Max", 5)
  4. self.name ← Max, self.age ← 5

    pass 2 of 2
    5class Dog:6    def __init__(self⟨Dog B⟩, nameMax, age5):7        self.name→ Max = nameMax8        self.age→ 5 = age59        print(f"Creating dog: {nameMax}, age {age5}")
    outputCreating dog: Max, age 5
  5. max_dog ← ⟨Dog B⟩

    12buddy = Dog("Buddy", 3)13max_dog→ ⟨Dog B⟩ = Dog("Max", 5)1415print(f"\n{buddy.nameBuddy} is {buddy.age3} years old")16print(f"{max_dog.nameMax} is {max_dog.age5} years old")1718print("\n=== Default Values ===")1920class Cat:21    def __init__(self, name, color="orange"):22        self.name = name23        self.color = color2425# With and without default26garfield = Cat("Garfield")  # Uses default color27whiskers = Cat("Whiskers", "gray")
    output
    Buddy is 3 years old
    Max is 5 years old
    
    === Default Values ===
  6. self.name ← Garfield, self.color ← orange

    pass 1 of 2
    20class Cat:21    def __init__(self⟨Cat C⟩, nameGarfield, colororange="orange"):22        self.name→ Garfield = nameGarfield23        self.color→ orange = colororange
  7. garfield ← ⟨Cat C⟩

    25# With and without default26garfield→ ⟨Cat C⟩ = Cat("Garfield")  # Uses default color27whiskers = Cat("Whiskers", "gray")
  8. self.name ← Whiskers, self.color ← gray

    pass 2 of 2
    20class Cat:21    def __init__(self⟨Cat D⟩, nameWhiskers, colorgray="orange"):22        self.name→ Whiskers = nameWhiskers23        self.color→ gray = colorgray
  9. whiskers ← ⟨Cat D⟩

    26garfield = Cat("Garfield")  # Uses default color27whiskers→ ⟨Cat D⟩ = Cat("Whiskers", "gray")2829print(f"{garfield.nameGarfield} is {garfield.colororange}")30print(f"{whiskers.nameWhiskers} is {whiskers.colorgray}")3132print("\n=== Multiple Initialization Patterns ===")3334class Person:35    def __init__(self, name, age=0, city="Unknown"):36        self.name = name37        self.age = age38        self.city = city39    40    def display(self):41        print(f"{self.name}, {self.age}, from {self.city}")4243# Various ways to create44p1 = Person("Alice")45p2 = Person("Bob", 25)
    outputGarfield is orange
    Whiskers is gray
    
    === Multiple Initialization Patterns ===
  10. self.name ← Alice, self.age ← 0, self.city ← Unknown

    pass 1 of 4
    34class Person:35    def __init__(self⟨Person E⟩, nameAlice, age0=0, cityUnknown="Unknown"):36        self.name→ Alice = nameAlice37        self.age→ 0 = age038        self.city→ Unknown = cityUnknown
    All 4 passes — pass 1 is the card above
    passselfnameagecityself.nameself.ageself.city
    1⟨Person E⟩Alice0UnknownAlice0Unknown
    2⟨Person F⟩Bob25UnknownBob25Unknown
    3⟨Person G⟩Carol30NYCCarol30NYC
    4⟨Person H⟩Dave0LADave0LA
  11. p1 ← ⟨Person E⟩

    43# Various ways to create44p1→ ⟨Person E⟩ = Person("Alice")45p2 = Person("Bob", 25)46p3 = Person("Carol", 30, "NYC")
  12. p2 ← ⟨Person F⟩

    44p1 = Person("Alice")45p2→ ⟨Person F⟩ = Person("Bob", 25)46p3 = Person("Carol", 30, "NYC")47p4 = Person("Dave", city="LA")
  13. p3 ← ⟨Person G⟩

    45p2 = Person("Bob", 25)46p3→ ⟨Person G⟩ = Person("Carol", 30, "NYC")47p4 = Person("Dave", city="LA")
  14. p4 ← ⟨Person H⟩

    46p3 = Person("Carol", 30, "NYC")47p4→ ⟨Person H⟩ = Person("Dave", city="LA")4849p1⟨Person E⟩.display()50p2.display()
  15. def display(self):

    pass 1 of 4
    40def display(self⟨Person E⟩):41    print(f"{self.nameAlice}, {self.age0}, from {self.cityUnknown}")
    outputAlice, 0, from Unknown
    All 4 passes — pass 1 is the card above
    passselfself.nameself.ageself.city
    1⟨Person E⟩Alice0Unknown
    2⟨Person F⟩Bob25Unknown
    3⟨Person G⟩Carol30NYC
    4⟨Person H⟩Dave0LA
  16. p1.display()

    49p1⟨Person E⟩.display()50p2⟨Person F⟩.display()51p3.display()
  17. p2.display()

    49p1.display()50p2⟨Person F⟩.display()51p3⟨Person G⟩.display()52p4.display()
  18. p3.display()

    50p2.display()51p3⟨Person G⟩.display()52p4⟨Person H⟩.display()
  19. p4.display()

    51p3.display()52p4⟨Person H⟩.display()5354print("\n=== Computed Attributes ===")5556class Rectangle:57    def __init__(self, width, height):58        self.width = width59        self.height = height60        self.area = width * height61        self.perimeter = 2 * (width + height)6263rect = Rectangle(4, 4)64print(f"Rectangle {rect.width}x{rect.height}")
    output
    === Computed Attributes ===
  20. self.width ← 4, self.height ← 4, self.area ← 16, self.perimeter ← 16

    56class Rectangle:57    def __init__(self⟨Rectangle I⟩, width4, height4):58        self.width→ 4 = width459        self.height→ 4 = height460        self.area→ 16 = width4 * height461        self.perimeter→ 16 = 2 * (width4 + height4)
  21. rect ← ⟨Rectangle I⟩

    63rect→ ⟨Rectangle I⟩ = Rectangle(4, 4)64print(f"Rectangle {rect.width4}x{rect.height4}")65print(f"Area: {rect.area16}")66print(f"Perimeter: {rect.perimeter16}")
    outputRectangle 4x4
    Area: 16
    Perimeter: 16
  1. print("=== The __init__ Method === ")

    3print("=== The __init__ Method ===\n")45class Dog:6    def __init__(self, name, age):7        self.name = name8        self.age = age9        print(f"Creating dog: {name}, age {age}")1011# Create dogs12buddy = Dog("Buddy", 3)13max_dog = Dog("Max", 5)
    output=== The __init__ Method ===
  2. self.name ← Buddy, self.age ← 3

    pass 1 of 2
    5class Dog:6    def __init__(self⟨Dog A⟩, nameBuddy, age3):7        self.name→ Buddy = nameBuddy8        self.age→ 3 = age39        print(f"Creating dog: {nameBuddy}, age {age3}")
    outputCreating dog: Buddy, age 3
  3. buddy ← ⟨Dog A⟩

    11# Create dogs12buddy→ ⟨Dog A⟩ = Dog("Buddy", 3)13max_dog = Dog("Max", 5)
  4. self.name ← Max, self.age ← 5

    pass 2 of 2
    5class Dog:6    def __init__(self⟨Dog B⟩, nameMax, age5):7        self.name→ Max = nameMax8        self.age→ 5 = age59        print(f"Creating dog: {nameMax}, age {age5}")
    outputCreating dog: Max, age 5
  5. max_dog ← ⟨Dog B⟩

    12buddy = Dog("Buddy", 3)13max_dog→ ⟨Dog B⟩ = Dog("Max", 5)1415print(f"\n{buddy.nameBuddy} is {buddy.age3} years old")16print(f"{max_dog.nameMax} is {max_dog.age5} years old")1718print("\n=== Default Values ===")1920class Cat:21    def __init__(self, name, color="orange"):22        self.name = name23        self.color = color2425# With and without default26garfield = Cat("Garfield")  # Uses default color27whiskers = Cat("Whiskers", "gray")
    output
    Buddy is 3 years old
    Max is 5 years old
    
    === Default Values ===
  6. self.name ← Garfield, self.color ← orange

    pass 1 of 2
    20class Cat:21    def __init__(self⟨Cat C⟩, nameGarfield, colororange="orange"):22        self.name→ Garfield = nameGarfield23        self.color→ orange = colororange
  7. garfield ← ⟨Cat C⟩

    25# With and without default26garfield→ ⟨Cat C⟩ = Cat("Garfield")  # Uses default color27whiskers = Cat("Whiskers", "gray")
  8. self.name ← Whiskers, self.color ← gray

    pass 2 of 2
    20class Cat:21    def __init__(self⟨Cat D⟩, nameWhiskers, colorgray="orange"):22        self.name→ Whiskers = nameWhiskers23        self.color→ gray = colorgray
  9. whiskers ← ⟨Cat D⟩

    26garfield = Cat("Garfield")  # Uses default color27whiskers→ ⟨Cat D⟩ = Cat("Whiskers", "gray")2829print(f"{garfield.nameGarfield} is {garfield.colororange}")30print(f"{whiskers.nameWhiskers} is {whiskers.colorgray}")3132print("\n=== Multiple Initialization Patterns ===")3334class Person:35    def __init__(self, name, age=0, city="Unknown"):36        self.name = name37        self.age = age38        self.city = city39    40    def display(self):41        print(f"{self.name}, {self.age}, from {self.city}")4243# Various ways to create44p1 = Person("Alice")45p2 = Person("Bob", 25)
    outputGarfield is orange
    Whiskers is gray
    
    === Multiple Initialization Patterns ===
  10. self.name ← Alice, self.age ← 0, self.city ← Unknown

    pass 1 of 4
    34class Person:35    def __init__(self⟨Person E⟩, nameAlice, age0=0, cityUnknown="Unknown"):36        self.name→ Alice = nameAlice37        self.age→ 0 = age038        self.city→ Unknown = cityUnknown
    All 4 passes — pass 1 is the card above
    passselfnameagecityself.nameself.ageself.city
    1⟨Person E⟩Alice0UnknownAlice0Unknown
    2⟨Person F⟩Bob25UnknownBob25Unknown
    3⟨Person G⟩Carol30NYCCarol30NYC
    4⟨Person H⟩Dave0LADave0LA
  11. p1 ← ⟨Person E⟩

    43# Various ways to create44p1→ ⟨Person E⟩ = Person("Alice")45p2 = Person("Bob", 25)46p3 = Person("Carol", 30, "NYC")
  12. p2 ← ⟨Person F⟩

    44p1 = Person("Alice")45p2→ ⟨Person F⟩ = Person("Bob", 25)46p3 = Person("Carol", 30, "NYC")47p4 = Person("Dave", city="LA")
  13. p3 ← ⟨Person G⟩

    45p2 = Person("Bob", 25)46p3→ ⟨Person G⟩ = Person("Carol", 30, "NYC")47p4 = Person("Dave", city="LA")
  14. p4 ← ⟨Person H⟩

    46p3 = Person("Carol", 30, "NYC")47p4→ ⟨Person H⟩ = Person("Dave", city="LA")4849p1⟨Person E⟩.display()50p2.display()
  15. def display(self):

    pass 1 of 4
    40def display(self⟨Person E⟩):41    print(f"{self.nameAlice}, {self.age0}, from {self.cityUnknown}")
    outputAlice, 0, from Unknown
    All 4 passes — pass 1 is the card above
    passselfself.nameself.ageself.city
    1⟨Person E⟩Alice0Unknown
    2⟨Person F⟩Bob25Unknown
    3⟨Person G⟩Carol30NYC
    4⟨Person H⟩Dave0LA
  16. p1.display()

    49p1⟨Person E⟩.display()50p2⟨Person F⟩.display()51p3.display()
  17. p2.display()

    49p1.display()50p2⟨Person F⟩.display()51p3⟨Person G⟩.display()52p4.display()
  18. p3.display()

    50p2.display()51p3⟨Person G⟩.display()52p4⟨Person H⟩.display()
  19. p4.display()

    51p3.display()52p4⟨Person H⟩.display()5354print("\n=== Computed Attributes ===")5556class Rectangle:57    def __init__(self, width, height):58        self.width = width59        self.height = height60        self.area = width * height61        self.perimeter = 2 * (width + height)6263rect = Rectangle(8, 2)64print(f"Rectangle {rect.width}x{rect.height}")
    output
    === Computed Attributes ===
  20. self.width ← 8, self.height ← 2, self.area ← 16, self.perimeter ← 20

    56class Rectangle:57    def __init__(self⟨Rectangle I⟩, width8, height2):58        self.width→ 8 = width859        self.height→ 2 = height260        self.area→ 16 = width8 * height261        self.perimeter→ 20 = 2 * (width8 + height2)
  21. rect ← ⟨Rectangle I⟩

    63rect→ ⟨Rectangle I⟩ = Rectangle(8, 2)64print(f"Rectangle {rect.width8}x{rect.height2}")65print(f"Area: {rect.area16}")66print(f"Perimeter: {rect.perimeter20}")
    outputRectangle 8x2
    Area: 16
    Perimeter: 20

Attributes set in __init__ are unique to each object.

attribute Data stored on an object: `self.name = "Alice"`. Accessed via `obj.name`.

Instance methods

Methods that operate on object data.

methods.py
Replay: real traced execution (multi-file project)
# Instance Methods

print("=== Instance Methods ===\n")

class Dog:
    def __init__(self, name, energy=100):
        self.name = name
        self.energy = energy

    def bark(self):
        print(f"{self.name} says: Woof! Woof!")

    def play(self):
        if self.energy >= 20:
            self.energy -= 20
            print(f"{self.name} plays! Energy: {self.energy}")
        else:
            print(f"{self.name} is too tired to play")

    def rest(self):
        self.energy = min(100, self.energy + 30)
        print(f"{self.name} rests. Energy: {self.energy}")

    def status(self):
        return f"{self.name}: {self.energy}/100 energy"

# Use methods
buddy = Dog("Buddy")
buddy.bark()
print(buddy.status())

print("\n=== Method Chaining State ===")

buddy.play()
buddy.play()
buddy.play()
buddy.play()  # Energy check
buddy.rest()
print(buddy.status())

print("\n=== Methods Returning Values ===")

class Calculator:
    def __init__(self, initial=0):
        self.value = initial

    def add(self, n):
        self.value += n
        return self.value

    def multiply(self, n):
        self.value *= n
        return self.value

    def reset(self):
        self.value = 0
        return self.value

calc = Calculator(10)
print(f"Start: {calc.value}")
print(f"Add 5: {calc.add(5)}")
print(f"Multiply by 3: {calc.multiply(3)}")
print(f"Current: {calc.value}")

print("\n=== Methods Calling Other Methods ===")

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

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

    def withdraw(self, amount):
        if amount <= self.balance:
            self.balance -= amount
            self._log_transaction("withdraw", amount)
            return True
        return False

    def _log_transaction(self, type, amount):
        print(f"[LOG] {self.owner}: {type} ${amount}, balance ${self.balance}")

account = BankAccount("Alice", 100)
account.deposit(50)
account.withdraw(30)
  1. print("=== Instance Methods === ")

    3print("=== Instance Methods ===\n")45class Dog:6    def __init__(self, name, energy=100):7        self.name = name8        self.energy = energy9    10    def bark(self):  #?method11        print(f"{self.name} says: Woof! Woof!")12    13    def play(self):  #?modifyattr14        if self.energy >= 20:15            self.energy -= 2016            print(f"{self.name} plays! Energy: {self.energy}")17        else:18            print(f"{self.name} is too tired to play")19    20    def rest(self):21        self.energy = min(100, self.energy + 30)  #?capvalue22        print(f"{self.name} rests. Energy: {self.energy}")23    24    def status(self):  #?returnmethod25        return f"{self.name}: {self.energy}/100 energy"2627# Use methods  #?usemethods28buddy = Dog("Buddy")29buddy.bark()
    output=== Instance Methods ===
  2. self.name ← Buddy, self.energy ← 100

    5class Dog:6    def __init__(self⟨Dog A⟩, nameBuddy, energy100=100):7        self.name→ Buddy = nameBuddy8        self.energy→ 100 = energy100
  3. buddy ← ⟨Dog A⟩

    27# Use methods  #?usemethods28buddy→ ⟨Dog A⟩ = Dog("Buddy")29buddy⟨Dog A⟩.bark()30print(buddy.status())
  4. def bark(self): #?method

    10def bark(self⟨Dog A⟩):  #?method11    print(f"{self.nameBuddy} says: Woof! Woof!")
    outputBuddy says: Woof! Woof!
  5. buddy.bark()

    28buddy = Dog("Buddy")29buddy⟨Dog A⟩.bark()30print(buddy⟨Dog A⟩.status())
  6. def status(self): #?returnmethod

    pass 1 of 2
    24def status(self⟨Dog A⟩):  #?returnmethod25    return f"{self.nameBuddy}: {self.energy100}/100 energy"
  7. print(buddy.status())

    29buddy.bark()30print(buddy⟨Dog A⟩.status())3132print("\n=== Method Chaining State ===")3334buddy⟨Dog A⟩.play()35buddy.play()
    outputBuddy: 100/100 energy
    
    === Method Chaining State ===
  8. def play(self): #?modifyattr

    pass 1 of 4
    13def play(self⟨Dog A⟩):  #?modifyattr14    if self.energy >= 20:15        self.energy -= 20
  9. self.energy ← 80

    pass 1 of 4
    13def play(self):  #?modifyattr14    if self.energy100 >= 20:15        self.energy→ 80 -= 2016        print(f"{self.nameBuddy} plays! Energy: {self.energy80}")17    else:
    outputBuddy plays! Energy: 80
    All 4 passes — pass 1 is the card above
    passself.energy
    1100 80
    280 60
    360 40
    440 20
  10. buddy.play()

    34buddy⟨Dog A⟩.play()35buddy⟨Dog A⟩.play()36buddy.play()
  11. buddy.play()

    34buddy.play()35buddy⟨Dog A⟩.play()36buddy⟨Dog A⟩.play()37buddy.play()  # Energy check  #?energycheck
  12. buddy.play()

    35buddy.play()36buddy⟨Dog A⟩.play()37buddy⟨Dog A⟩.play()  # Energy check  #?energycheck38buddy.rest()
  13. buddy.play() # Energy check #?energycheck

    36buddy.play()37buddy⟨Dog A⟩.play()  # Energy check  #?energycheck38buddy⟨Dog A⟩.rest()39print(buddy.status())
  14. self.energy ← 50

    20def rest(self⟨Dog A⟩):21    self.energy→ 50 = min(100, self.energy + 30)  #?capvalue22    print(f"{self.nameBuddy} rests. Energy: {self.energy50}")
    outputBuddy rests. Energy: 50
  15. buddy.rest()

    37buddy.play()  # Energy check  #?energycheck38buddy⟨Dog A⟩.rest()39print(buddy⟨Dog A⟩.status())
  16. def status(self): #?returnmethod

    pass 2 of 2
    24def status(self⟨Dog A⟩):  #?returnmethod25    return f"{self.nameBuddy}: {self.energy50}/100 energy"
  17. print(buddy.status())

    38buddy.rest()39print(buddy⟨Dog A⟩.status())4041print("\n=== Methods Returning Values ===")4243class Calculator:44    def __init__(self, initial=0):45        self.value = initial46    47    def add(self, n):  #?returnself48        self.value += n49        return self.value  #?returnvalue50    51    def multiply(self, n):52        self.value *= n53        return self.value54    55    def reset(self):56        self.value = 057        return self.value5859calc = Calculator(10)60print(f"Start: {calc.value}")
    outputBuddy: 50/100 energy
    
    === Methods Returning Values ===
  18. self.value ← 10

    43class Calculator:44    def __init__(self⟨Calculator B⟩, initial10=0):45        self.value→ 10 = initial10
  19. calc ← ⟨Calculator B⟩

    59calc→ ⟨Calculator B⟩ = Calculator(10)60print(f"Start: {calc.value10}")61print(f"Add 5: {calc⟨Calculator B⟩.add(5)}")62print(f"Multiply by 3: {calc.multiply(3)}")
    outputStart: 10
  20. self.value ← 15

    47def add(self⟨Calculator B⟩, n5):  #?returnself48    self.value→ 15 += n549    return self.value15  #?returnvalue
  21. print(f"Add 5: {calc.add(5)}")

    60print(f"Start: {calc.value}")61print(f"Add 5: {calc⟨Calculator B⟩.add(5)}")62print(f"Multiply by 3: {calc⟨Calculator B⟩.multiply(3)}")63print(f"Current: {calc.value}")
    outputAdd 5: 15
  22. self.value ← 45

    51def multiply(self⟨Calculator B⟩, n3):52    self.value→ 45 *= n353    return self.value45
  23. print(f"Multiply by 3: {calc.multiply(3)}")

    61print(f"Add 5: {calc.add(5)}")62print(f"Multiply by 3: {calc⟨Calculator B⟩.multiply(3)}")63print(f"Current: {calc.value45}")6465print("\n=== Methods Calling Other Methods ===")6667class BankAccount:68    def __init__(self, owner, balance=0):69        self.owner = owner70        self.balance = balance71    72    def deposit(self, amount):73        self.balance += amount74        self._log_transaction("deposit", amount)  #?callmethod75    76    def withdraw(self, amount):77        if amount <= self.balance:78            self.balance -= amount79            self._log_transaction("withdraw", amount)80            return True81        return False82    83    def _log_transaction(self, type, amount):  #?internalmethod84        print(f"[LOG] {self.owner}: {type} ${amount}, balance ${self.balance}")8586account = BankAccount("Alice", 100)87account.deposit(50)
    outputMultiply by 3: 45
    Current: 45
    
    === Methods Calling Other Methods ===
  24. self.owner ← Alice, self.balance ← 100

    67class BankAccount:68    def __init__(self⟨BankAccount C⟩, ownerAlice, balance100=0):69        self.owner→ Alice = ownerAlice70        self.balance→ 100 = balance100
  25. account ← ⟨BankAccount C⟩

    86account→ ⟨BankAccount C⟩ = BankAccount("Alice", 100)87account⟨BankAccount C⟩.deposit(50)88account.withdraw(30)
  26. self.balance ← 150

    72def deposit(self⟨BankAccount C⟩, amount50):73    self.balance→ 150 += amount5074    self._log_transaction("deposit", amount50)  #?callmethod
  27. def _log_transaction(self, type, amount): #?internalmethod

    pass 1 of 2
    73    self.balance += amount74    self._log_transaction("deposit", amount50)  #?callmethod7576def withdraw(self, amount):77    if amount <= self.balance:78        self.balance -= amount79        self._log_transaction("withdraw", amount)80        return True81    return False8283def _log_transaction(self⟨BankAccount C⟩, typedeposit, amount50):  #?internalmethod84    print(f"[LOG] {self.ownerAlice}: {type} ${amount50}, balance ${self.balance150}")
    output[LOG] Alice: deposit $50, balance $150
  28. account.deposit(50)

    86account = BankAccount("Alice", 100)87account⟨BankAccount C⟩.deposit(50)88account⟨BankAccount C⟩.withdraw(30)89#@help method
  29. def withdraw(self, amount):

    76def withdraw(self⟨BankAccount C⟩, amount30):77    if amount <= self.balance:78        self.balance -= amount
  30. self.balance ← 120

    76def withdraw(self, amount):77    if amount30 <= self.balance150:78        self.balance→ 120 -= amount3079        self._log_transaction("withdraw", amount30)80        return True
  31. def _log_transaction(self, type, amount): #?internalmethod

    pass 2 of 2
    78        self.balance -= amount79        self._log_transaction("withdraw", amount30)80        return True81    return False8283def _log_transaction(self⟨BankAccount C⟩, typewithdraw, amount30):  #?internalmethod84    print(f"[LOG] {self.ownerAlice}: {type} ${amount30}, balance ${self.balance120}")
    output[LOG] Alice: withdraw $30, balance $120
  32. account.withdraw(30)

    87account.deposit(50)88account⟨BankAccount C⟩.withdraw(30)89#@help method

Methods take self as first parameter to access the object's attributes.

method Function defined inside a class. First parameter is `self`.

Multiple objects

Each object is independent with its own data.

multiple_objects.py
Replay: real traced execution (multi-file project)
# Working with Multiple Objects

print("=== Multiple Objects ===\n")

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def study(self, hours):
        improvement = hours * 2
        self.grade = min(100, self.grade + improvement)

    def display(self):
        print(f"{self.name}: {self.grade}/100")

# Create multiple students
alice = Student("Alice", 85)
bob = Student("Bob", 72)
carol = Student("Carol", 90)

# Each has own state
print("Initial grades:")
alice.display()
bob.display()
carol.display()

print("\n=== Objects Act Independently ===")

# Changes to one don't affect others
bob.study(5)  # Bob studies
carol.study(3)

print("\nAfter studying:")
alice.display()  # Unchanged
bob.display()    # Improved by 10
carol.display()  # Improved by 6

print("\n=== Objects in a List ===")

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def apply_discount(self, percent):
        self.price *= (1 - percent/100)

# Store objects in list
products = [
    Product("Laptop", 999),
    Product("Phone", 699),
    Product("Tablet", 499)
]

# Process all objects
print("Original prices:")
for p in products:
    print(f"  {p.name}: ${p.price}")

# Apply discount to all
for p in products:
    p.apply_discount(10)

print("\nAfter 10% discount:")
for p in products:
    print(f"  {p.name}: ${p.price:.2f}")

print("\n=== Objects Interacting ===")

class Player:
    def __init__(self, name, health=100):
        self.name = name
        self.health = health

    def attack(self, other, damage):
        other.health -= damage
        print(f"{self.name} attacks {other.name} for {damage} damage!")
        print(f"  {other.name}'s health: {other.health}")

    def is_alive(self):
        return self.health > 0

# Two players interact
hero = Player("Hero")
monster = Player("Dragon", 150)

hero.attack(monster, 30)
monster.attack(hero, 25)
hero.attack(monster, 40)

print(f"\nFinal: Hero={hero.health}, Dragon={monster.health}")
  1. print("=== Multiple Objects === ")

    3print("=== Multiple Objects ===\n")45class Student:6    def __init__(self, name, grade):7        self.name = name8        self.grade = grade9    10    def study(self, hours):11        improvement = hours * 2  #?improvement12        self.grade = min(100, self.grade + improvement)13    14    def display(self):15        print(f"{self.name}: {self.grade}/100")1617# Create multiple students  #?multiplestudents18alice = Student("Alice", 85)19bob = Student("Bob", 72)
    output=== Multiple Objects ===
  2. self.name ← Alice, self.grade ← 85

    pass 1 of 3
    5class Student:6    def __init__(self⟨Student A⟩, nameAlice, grade85):7        self.name→ Alice = nameAlice8        self.grade→ 85 = grade85
    All 3 passes — pass 1 is the card above
    passselfnamegradeself.nameself.grade
    1⟨Student A⟩Alice85Alice85
    2⟨Student B⟩Bob72Bob72
    3⟨Student C⟩Carol90Carol90
  3. alice ← ⟨Student A⟩

    17# Create multiple students  #?multiplestudents18alice→ ⟨Student A⟩ = Student("Alice", 85)19bob = Student("Bob", 72)20carol = Student("Carol", 90)
  4. bob ← ⟨Student B⟩

    18alice = Student("Alice", 85)19bob→ ⟨Student B⟩ = Student("Bob", 72)20carol = Student("Carol", 90)
  5. carol ← ⟨Student C⟩

    19bob = Student("Bob", 72)20carol→ ⟨Student C⟩ = Student("Carol", 90)2122# Each has own state  #?ownstate23print("Initial grades:")24alice⟨Student A⟩.display()25bob.display()
    outputInitial grades:
  6. def display(self):

    pass 1 of 6
    14def display(self⟨Student A⟩):15    print(f"{self.nameAlice}: {self.grade85}/100")
    outputAlice: 85/100
    All 6 passes — pass 1 is the card above
    passselfself.nameself.grade
    1⟨Student A⟩Alice85
    2⟨Student B⟩Bob72
    3⟨Student C⟩Carol90
    4⟨Student A⟩Alice85
    5⟨Student B⟩Bob82
    6⟨Student C⟩Carol96
  7. alice.display()

    23print("Initial grades:")24alice⟨Student A⟩.display()25bob⟨Student B⟩.display()26carol.display()
  8. bob.display()

    24alice.display()25bob⟨Student B⟩.display()26carol⟨Student C⟩.display()
  9. carol.display()

    25bob.display()26carol⟨Student C⟩.display()2728print("\n=== Objects Act Independently ===")2930# Changes to one don't affect others  #?independent31bob⟨Student B⟩.study(5)  # Bob studies32carol.study(3)
    output
    === Objects Act Independently ===
  10. improvement ← 10, self.grade ← 82

    pass 1 of 2
    10def study(self⟨Student B⟩, hours5):11    improvement→ 10 = hours5 * 2  #?improvement12    self.grade→ 82 = min(100, self.grade + improvement10)
  11. bob.study(5) # Bob studies

    30# Changes to one don't affect others  #?independent31bob⟨Student B⟩.study(5)  # Bob studies32carol⟨Student C⟩.study(3)
  12. improvement ← 6, self.grade ← 96

    pass 2 of 2
    10def study(self⟨Student C⟩, hours3):11    improvement→ 6 = hours3 * 2  #?improvement12    self.grade→ 96 = min(100, self.grade + improvement6)
  13. carol.study(3)

    31bob.study(5)  # Bob studies32carol⟨Student C⟩.study(3)3334print("\nAfter studying:")35alice⟨Student A⟩.display()  # Unchanged36bob.display()    # Improved by 10
    output
    After studying:
  14. alice.display() # Unchanged

    34print("\nAfter studying:")35alice⟨Student A⟩.display()  # Unchanged36bob⟨Student B⟩.display()    # Improved by 1037carol.display()  # Improved by 6
  15. bob.display() # Improved by 10

    35alice.display()  # Unchanged36bob⟨Student B⟩.display()    # Improved by 1037carol⟨Student C⟩.display()  # Improved by 6
  16. carol.display() # Improved by 6

    36bob.display()    # Improved by 1037carol⟨Student C⟩.display()  # Improved by 63839print("\n=== Objects in a List ===")4041class Product:42    def __init__(self, name, price):43        self.name = name44        self.price = price45    46    def apply_discount(self, percent):47        self.price *= (1 - percent/100)4849# Store objects in list  #?objectlist50products = [51    Product("Laptop", 999),52    Product("Phone", 699),53    Product("Tablet", 499)54]
    output
    === Objects in a List ===
  17. self.name ← Laptop, self.price ← 999

    pass 1 of 3
    41class Product:42    def __init__(self⟨Product D⟩, nameLaptop, price999):43        self.name→ Laptop = nameLaptop44        self.price→ 999 = price999
    All 3 passes — pass 1 is the card above
    passselfnamepriceself.nameself.price
    1⟨Product D⟩Laptop999Laptop999
    2⟨Product E⟩Phone699Phone699
    3⟨Product F⟩Tablet499Tablet499
  18. products ← [⟨Product D⟩, ⟨Product E⟩, ⟨Product F⟩]

    49# Store objects in list  #?objectlist50products→ [⟨Product D⟩, ⟨Product E⟩, ⟨Product F⟩] = [51    Product("Laptop", 999),52    Product("Phone", 699),53    Product("Tablet", 499)54]5556# Process all objects  #?processall57print("Original prices:")58for p in products:
    outputOriginal prices:
  19. for p in products:

    pass 1 of 3
    57print("Original prices:")58for p⟨Product D⟩ in products[⟨Product D⟩, ⟨Product E⟩, ⟨Product F⟩]:59    print(f"  {p.nameLaptop}: ${p.price999}")
    output  Laptop: $999
    All 3 passes — pass 1 is the card above
    passpp.namep.price
    1⟨Product D⟩Laptop999
    2⟨Product E⟩Phone699
    3⟨Product F⟩Tablet499
  20. for p in products:

    pass 1 of 3
    61# Apply discount to all  #?bulkoperation62for p⟨Product D⟩ in products[⟨Product D⟩, ⟨Product E⟩, ⟨Product F⟩]:63    p⟨Product D⟩.apply_discount(10)
    All 3 passes — pass 1 is the card above
    passp
    1⟨Product D⟩
    2⟨Product E⟩
    3⟨Product F⟩
  21. self.price ← 899.1

    pass 1 of 3
    46def apply_discount(self⟨Product D⟩, percent10):47    self.price→ 899.1 *= (1 - percent10/100)
    All 3 passes — pass 1 is the card above
    passselfself.price
    1⟨Product D⟩999 899.1
    2⟨Product E⟩699 629.1
    3⟨Product F⟩499 449.1
  22. p.apply_discount(10)

    62for p in products:63    p⟨Product D⟩.apply_discount(10)
  23. p.apply_discount(10)

    62for p in products:63    p⟨Product E⟩.apply_discount(10)
  24. p.apply_discount(10)

    62for p in products:63    p⟨Product F⟩.apply_discount(10)
  25. print(" After 10% discount:")

    65print("\nAfter 10% discount:")66for p in products:
    output
    After 10% discount:
  26. for p in products:

    pass 1 of 3
    65print("\nAfter 10% discount:")66for p⟨Product D⟩ in products[⟨Product D⟩, ⟨Product E⟩, ⟨Product F⟩]:67    print(f"  {p.nameLaptop}: ${p.price899.1:.2f}")
    output  Laptop: $899.10
    All 3 passes — pass 1 is the card above
    passpp.namep.price
    1⟨Product D⟩Laptop899.1
    2⟨Product E⟩Phone629.1
    3⟨Product F⟩Tablet449.1
  27. print(" === Objects Interacting ===")

    69print("\n=== Objects Interacting ===")7071class Player:72    def __init__(self, name, health=100):73        self.name = name74        self.health = health75    76    def attack(self, other, damage):  #?interact77        other.health -= damage  #?modifyother78        print(f"{self.name} attacks {other.name} for {damage} damage!")79        print(f"  {other.name}'s health: {other.health}")80    81    def is_alive(self):82        return self.health > 08384# Two players interact  #?twoplayers85hero = Player("Hero")86monster = Player("Dragon", 150)
    output
    === Objects Interacting ===
  28. self.name ← Hero, self.health ← 100

    pass 1 of 2
    71class Player:72    def __init__(self⟨Player G⟩, nameHero, health100=100):73        self.name→ Hero = nameHero74        self.health→ 100 = health100
  29. hero ← ⟨Player G⟩

    84# Two players interact  #?twoplayers85hero→ ⟨Player G⟩ = Player("Hero")86monster = Player("Dragon", 150)
  30. self.name ← Dragon, self.health ← 150

    pass 2 of 2
    71class Player:72    def __init__(self⟨Player H⟩, nameDragon, health150=100):73        self.name→ Dragon = nameDragon74        self.health→ 150 = health150
  31. monster ← ⟨Player H⟩

    85hero = Player("Hero")86monster→ ⟨Player H⟩ = Player("Dragon", 150)8788hero⟨Player G⟩.attack(monster⟨Player H⟩, 30)89monster.attack(hero, 25)
  32. other.health ← 120

    pass 1 of 3
    76def attack(self⟨Player G⟩, other⟨Player H⟩, damage30):  #?interact77    other.health→ 120 -= damage30  #?modifyother78    print(f"{self.nameHero} attacks {other.nameDragon} for {damage30} damage!")79    print(f"  {other.nameDragon}'s health: {other.health120}")
    outputHero attacks Dragon for 30 damage!
      Dragon's health: 120
    All 3 passes — pass 1 is the card above
    passselfotherdamageself.nameother.nameother.health
    1⟨Player G⟩⟨Player H⟩30HeroDragon150 120
    2⟨Player H⟩⟨Player G⟩25DragonHero100 75
    3⟨Player G⟩⟨Player H⟩40HeroDragon120 80
  33. hero.attack(monster, 30)

    88hero⟨Player G⟩.attack(monster⟨Player H⟩, 30)89monster⟨Player H⟩.attack(hero⟨Player G⟩, 25)90hero.attack(monster, 40)
  34. monster.attack(hero, 25)

    88hero.attack(monster, 30)89monster⟨Player H⟩.attack(hero⟨Player G⟩, 25)90hero⟨Player G⟩.attack(monster⟨Player H⟩, 40)
  35. hero.attack(monster, 40)

    89monster.attack(hero, 25)90hero⟨Player G⟩.attack(monster⟨Player H⟩, 40)9192print(f"\nFinal: Hero={hero.health75}, Dragon={monster.health80}")93#@help improvement
    output
    Final: Hero=75, Dragon=80

Changes to one object don't affect others. Each has its own memory.

Classes with business logic

Methods that compute and transform data.

class_with_logic.py
Replay: real traced execution (multi-file project)
# Classes with Business Logic

print("=== Shopping Cart ===\n")

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add_item(self, name, price, quantity=1):
        item = {"name": name, "price": price, "qty": quantity}
        self.items.append(item)
        print(f"Added {quantity}x {name} @ ${price}")

    def remove_item(self, name):
        for item in self.items:
            if item["name"] == name:
                self.items.remove(item)
                print(f"Removed {name}")
                return True
        print(f"{name} not found")
        return False

    def get_total(self):
        total = 0
        for item in self.items:
            total += item["price"] * item["qty"]
        return total

    def display(self):
        print("\n--- Cart Contents ---")
        for item in self.items:
            subtotal = item["price"] * item["qty"]
            print(f"  {item['name']}: {item['qty']} x ${item['price']} = ${subtotal}")
        print(f"Total: ${self.get_total():.2f}")

# Use the cart
cart = ShoppingCart()
cart.add_item("Apple", 0.50, 5)
cart.add_item("Bread", 2.50)
cart.add_item("Milk", 3.00, 2)
cart.display()

cart.remove_item("Bread")
cart.display()

print("\n=== Temperature Converter ===")

class Temperature:
    def __init__(self, celsius=0):
        self.celsius = celsius

    @property
    def fahrenheit(self):
        return self.celsius * 9/5 + 32

    @property
    def kelvin(self):
        return self.celsius + 273.15

    def set_fahrenheit(self, f):
        self.celsius = (f - 32) * 5/9

    def describe(self):
        if self.celsius < 0:
            return "Freezing"
        elif self.celsius < 15:
            return "Cold"
        elif self.celsius < 25:
            return "Comfortable"
        else:
            return "Hot"

temp = Temperature(25)
print(f"{temp.celsius}°C = {temp.fahrenheit}°F = {temp.kelvin}K")
print(f"Feeling: {temp.describe()}")

temp.set_fahrenheit(32)  # Set from Fahrenheit
print(f"\n32°F = {temp.celsius:.1f}°C ({temp.describe()})")

print("\n=== Counter with History ===")

class Counter:
    def __init__(self, start=0):
        self.value = start
        self.history = [start]

    def increment(self, amount=1):
        self.value += amount
        self.history.append(self.value)

    def decrement(self, amount=1):
        self.value -= amount
        self.history.append(self.value)

    def undo(self):
        if len(self.history) > 1:
            self.history.pop()
            self.value = self.history[-1]

counter = Counter()
counter.increment(5)
counter.increment(3)
counter.decrement(2)
print(f"Value: {counter.value}")
print(f"History: {counter.history}")

counter.undo()
print(f"After undo: {counter.value}")
print(f"History: {counter.history}")
  1. print("=== Shopping Cart === ")

    3print("=== Shopping Cart ===\n")45class ShoppingCart:6    def __init__(self):7        self.items = []  #?initlist8    9    def add_item(self, name, price, quantity=1):  #?additem10        item = {"name": name, "price": price, "qty": quantity}  #?itemdict11        self.items.append(item)12        print(f"Added {quantity}x {name} @ ${price}")13    14    def remove_item(self, name):15        for item in self.items:  #?searchitem16            if item["name"] == name:17                self.items.remove(item)18                print(f"Removed {name}")19                return True20        print(f"{name} not found")21        return False22    23    def get_total(self):  #?gettotal24        total = 025        for item in self.items:26            total += item["price"] * item["qty"]27        return total28    29    def display(self):30        print("\n--- Cart Contents ---")31        for item in self.items:32            subtotal = item["price"] * item["qty"]33            print(f"  {item['name']}: {item['qty']} x ${item['price']} = ${subtotal}")34        print(f"Total: ${self.get_total():.2f}")3536# Use the cart  #?usecart37cart = ShoppingCart()38cart.add_item("Apple", 0.50, 5)
    output=== Shopping Cart ===
  2. self.items ← []

    5class ShoppingCart:6    def __init__(self⟨ShoppingCart A⟩):7        self.items→ [] = []  #?initlist
  3. cart ← ⟨ShoppingCart A⟩

    36# Use the cart  #?usecart37cart→ ⟨ShoppingCart A⟩ = ShoppingCart()38cart⟨ShoppingCart A⟩.add_item("Apple", 0.50, 5)39cart.add_item("Bread", 2.50)
  4. item ← {'name': 'Apple', 'price': 0.5, 'qty': 5}, self.items ← [{'name': 'Apple', 'price': 0.5, 'qty': 5}]

    pass 1 of 3
    9def add_item(self⟨ShoppingCart A⟩, nameApple, price0.5, quantity5=1):  #?additem10    item→ {'name': 'Apple', 'price': 0.5, 'qty': 5} = {"name": nameApple, "price": price0.5, "qty": quantity5}  #?itemdict11    self.items→ [{'name': 'Apple', 'price': 0.5, 'qty': 5}].append(item{'name': 'Apple', 'price': 0.5, 'qty': 5})12    print(f"Added {quantity5}x {nameApple} @ ${price0.5}")
    outputAdded 5x Apple @ $0.5
    All 3 passes — pass 1 is the card above
    passnamepricequantityitemself.items
    1Apple0.55{'name': 'Apple', 'price': 0.5, 'qty': 5}[] [{'name': 'Apple', 'price': 0.5, 'qty': 5}]
    2Bread2.51{'name': 'Bread', 'price': 2.5, 'qty': 1}[{'name': 'Apple', 'price': 0.5, 'qty': 5}] [{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}]
    3Milk3.02{'name': 'Milk', 'price': 3.0, 'qty': 2}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}] [{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]
  5. cart.add_item("Apple", 0.50, 5)

    37cart = ShoppingCart()38cart⟨ShoppingCart A⟩.add_item("Apple", 0.50, 5)39cart⟨ShoppingCart A⟩.add_item("Bread", 2.50)40cart.add_item("Milk", 3.00, 2)
  6. cart.add_item("Bread", 2.50)

    38cart.add_item("Apple", 0.50, 5)39cart⟨ShoppingCart A⟩.add_item("Bread", 2.50)40cart⟨ShoppingCart A⟩.add_item("Milk", 3.00, 2)41cart.display()
  7. cart.add_item("Milk", 3.00, 2)

    39cart.add_item("Bread", 2.50)40cart⟨ShoppingCart A⟩.add_item("Milk", 3.00, 2)41cart⟨ShoppingCart A⟩.display()
  8. def display(self):

    pass 1 of 2
    29def display(self⟨ShoppingCart A⟩):30    print("\n--- Cart Contents ---")31    for item in self.items:
    output
    --- Cart Contents ---
  9. subtotal ← 2.5

    pass 1 of 5
    30print("\n--- Cart Contents ---")31for item{'name': 'Apple', 'price': 0.5, 'qty': 5} in self.items[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]:32    subtotal→ 2.5 = item["price"]0.5 * item["qty"]533    print(f"  {item['name']Apple}: {item['qty']5} x ${item['price']0.5} = ${subtotal2.5}")34print(f"Total: ${self.get_total():.2f}")
    output  Apple: 5 x $0.5 = $2.5
    All 5 passes — pass 1 is the card above
    passitemself.itemsitem[”price”]item[”qty”]item[’name’]item[’qty’]item[’price’]subtotal
    1{'name': 'Apple', 'price': 0.5, 'qty': 5}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]0.55Apple50.52.5
    2{'name': 'Bread', 'price': 2.5, 'qty': 1}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]2.51Bread12.52.5
    3{'name': 'Milk', 'price': 3.0, 'qty': 2}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]3.02Milk23.06.0
    4{'name': 'Apple', 'price': 0.5, 'qty': 5}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]0.55Apple50.52.5
    5{'name': 'Milk', 'price': 3.0, 'qty': 2}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]3.02Milk23.06.0
  10. print(f"Total: ${self.get_total():.2f}")

    33    print(f"  {item['name']}: {item['qty']} x ${item['price']} = ${subtotal}")34print(f"Total: ${self.get_total():.2f}")
  11. total ← 0

    pass 1 of 2
    23def get_total(self⟨ShoppingCart A⟩):  #?gettotal24    total→ 0 = 025    for item in self.items:
  12. total ← 2.5

    pass 1 of 5
    24total = 025for item{'name': 'Apple', 'price': 0.5, 'qty': 5} in self.items[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]:26    total→ 2.5 += item["price"]0.5 * item["qty"]527return total
    All 5 passes — pass 1 is the card above
    passitemself.itemsitem[”price”]item[”qty”]total
    1{'name': 'Apple', 'price': 0.5, 'qty': 5}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]0.550 2.5
    2{'name': 'Bread', 'price': 2.5, 'qty': 1}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]2.512.5 5.0
    3{'name': 'Milk', 'price': 3.0, 'qty': 2}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]3.025.0 11.0
    4{'name': 'Apple', 'price': 0.5, 'qty': 5}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]0.550 2.5
    5{'name': 'Milk', 'price': 3.0, 'qty': 2}[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]3.022.5 8.5
  13. return total

    26    total += item["price"] * item["qty"]27return total11.0
  14. print(f"Total: ${self.get_total():.2f}")

    33    print(f"  {item['name']}: {item['qty']} x ${item['price']} = ${subtotal}")34print(f"Total: ${self.get_total():.2f}")
    outputTotal: $11.00
  15. cart.display()

    40cart.add_item("Milk", 3.00, 2)41cart⟨ShoppingCart A⟩.display()4243cart⟨ShoppingCart A⟩.remove_item("Bread")44cart.display()
  16. def remove_item(self, name):

    14def remove_item(self⟨ShoppingCart A⟩, nameBread):15    for item in self.items:  #?searchitem16        if item["name"] == name:
  17. for item in self.items: #?searchitem

    pass 1 of 2
    14def remove_item(self, name):15    for item{'name': 'Apple', 'price': 0.5, 'qty': 5} in self.items[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]:  #?searchitem16        if item["name"] == name:17            self.items.remove(item)
  18. for item in self.items: #?searchitem

    pass 2 of 2
    14def remove_item(self, name):15    for item{'name': 'Bread', 'price': 2.5, 'qty': 1} in self.items[{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Bread', 'price': 2.5, 'qty': 1}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]:  #?searchitem16        if item["name"] == name:17            self.items.remove(item)
  19. self.items ← [{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Milk', 'price': 3.0, 'qty': 2}]

    15for item in self.items:  #?searchitem16    if item["name"]Bread == nameBread:17        self.items→ [{'name': 'Apple', 'price': 0.5, 'qty': 5}, {'name': 'Milk', 'price': 3.0, 'qty': 2}].remove(item{'name': 'Bread', 'price': 2.5, 'qty': 1})18        print(f"Removed {nameBread}")19        return True20print(f"{name} not found")
    outputRemoved Bread
  20. cart.remove_item("Bread")

    43cart⟨ShoppingCart A⟩.remove_item("Bread")44cart⟨ShoppingCart A⟩.display()
  21. def display(self):

    pass 2 of 2
    29def display(self⟨ShoppingCart A⟩):30    print("\n--- Cart Contents ---")31    for item in self.items:
    output
    --- Cart Contents ---
  22. print(f"Total: ${self.get_total():.2f}")

    33    print(f"  {item['name']}: {item['qty']} x ${item['price']} = ${subtotal}")34print(f"Total: ${self.get_total():.2f}")
  23. total ← 0

    pass 2 of 2
    23def get_total(self⟨ShoppingCart A⟩):  #?gettotal24    total→ 0 = 025    for item in self.items:
  24. return total

    26    total += item["price"] * item["qty"]27return total8.5
  25. print(f"Total: ${self.get_total():.2f}")

    33    print(f"  {item['name']}: {item['qty']} x ${item['price']} = ${subtotal}")34print(f"Total: ${self.get_total():.2f}")
    outputTotal: $8.50
  26. cart.display()

    43cart.remove_item("Bread")44cart⟨ShoppingCart A⟩.display()4546print("\n=== Temperature Converter ===")4748class Temperature:49    def __init__(self, celsius=0):  #?tempinit50        self.celsius = celsius51    52    @property53    def fahrenheit(self):  #?property54        return self.celsius * 9/5 + 3255    56    @property57    def kelvin(self):58        return self.celsius + 273.1559    60    def set_fahrenheit(self, f):  #?setf61        self.celsius = (f - 32) * 5/962    63    def describe(self):  #?describe64        if self.celsius < 0:65            return "Freezing"66        elif self.celsius < 15:67            return "Cold"68        elif self.celsius < 25:69            return "Comfortable"70        else:71            return "Hot"7273temp = Temperature(25)74print(f"{temp.celsius}°C = {temp.fahrenheit}°F = {temp.kelvin}K")
    output
    === Temperature Converter ===
  27. self.celsius ← 25

    48class Temperature:49    def __init__(self⟨Temperature B⟩, celsius25=0):  #?tempinit50        self.celsius→ 25 = celsius25
  28. temp ← ⟨Temperature B⟩

    73temp→ ⟨Temperature B⟩ = Temperature(25)74print(f"{temp.celsius25}°C = {temp.fahrenheit77.0}°F = {temp.kelvin298.15}K")75print(f"Feeling: {temp.describe()}")
  29. def fahrenheit(self): #?property

    52@property53def fahrenheit(self⟨Temperature B⟩):  #?property54    return self.celsius25 * 9/5 + 32
  30. def kelvin(self):

    56@property57def kelvin(self⟨Temperature B⟩):58    return self.celsius25 + 273.15
  31. print(f"{temp.celsius}°C = {temp.fahrenheit}°F = {temp.kelvin}K")

    73temp = Temperature(25)74print(f"{temp.celsius25}°C = {temp.fahrenheit77.0}°F = {temp.kelvin298.15}K")75print(f"Feeling: {temp⟨Temperature B⟩.describe()}")
    output25°C = 77.0°F = 298.15K
  32. def describe(self): #?describe

    pass 1 of 2
    63def describe(self⟨Temperature B⟩):  #?describe64    if self.celsius < 0:65        return "Freezing"
  33. print(f"Feeling: {temp.describe()}")

    74print(f"{temp.celsius}°C = {temp.fahrenheit}°F = {temp.kelvin}K")75print(f"Feeling: {temp⟨Temperature B⟩.describe()}")7677temp⟨Temperature B⟩.set_fahrenheit(32)  # Set from Fahrenheit  #?setfuse78print(f"\n32°F = {temp.celsius:.1f}°C ({temp.describe()})")
    outputFeeling: Hot
  34. self.celsius ← 0.0

    60def set_fahrenheit(self⟨Temperature B⟩, f32):  #?setf61    self.celsius→ 0.0 = (f32 - 32) * 5/9
  35. temp.set_fahrenheit(32) # Set from Fahrenheit #?setfuse

    77temp⟨Temperature B⟩.set_fahrenheit(32)  # Set from Fahrenheit  #?setfuse78print(f"\n32°F = {temp.celsius0.0:.1f}°C ({temp⟨Temperature B⟩.describe()})")
  36. def describe(self): #?describe

    pass 2 of 2
    63def describe(self⟨Temperature B⟩):  #?describe64    if self.celsius < 0:65        return "Freezing"
  37. elif self.celsius < 15:

    65    return "Freezing"66elif self.celsius0.0 < 15:67    return "Cold"68elif self.celsius < 25:
  38. print(f" 32°F = {temp.celsius:.1f}°C ({temp.describe()})")

    77temp.set_fahrenheit(32)  # Set from Fahrenheit  #?setfuse78print(f"\n32°F = {temp.celsius0.0:.1f}°C ({temp⟨Temperature B⟩.describe()})")7980print("\n=== Counter with History ===")8182class Counter:83    def __init__(self, start=0):84        self.value = start85        self.history = [start]  #?history86    87    def increment(self, amount=1):88        self.value += amount89        self.history.append(self.value)  #?record90    91    def decrement(self, amount=1):92        self.value -= amount93        self.history.append(self.value)94    95    def undo(self):  #?undo96        if len(self.history) > 1:97            self.history.pop()98            self.value = self.history[-1]99100counter = Counter()101counter.increment(5)
    output
    32°F = 0.0°C (Cold)
    
    === Counter with History ===
  39. self.value ← 0, self.history ← [0]

    82class Counter:83    def __init__(self⟨Counter C⟩, start0=0):84        self.value→ 0 = start085        self.history→ [0] = [start0]  #?history
  40. counter ← ⟨Counter C⟩

    100counter→ ⟨Counter C⟩ = Counter()101counter⟨Counter C⟩.increment(5)102counter.increment(3)
  41. self.value ← 5, self.history ← [0, 5]

    pass 1 of 2
    87def increment(self⟨Counter C⟩, amount5=1):88    self.value→ 5 += amount589    self.history→ [0, 5].append(self.value5)  #?record
  42. counter.increment(5)

    100counter = Counter()101counter⟨Counter C⟩.increment(5)102counter⟨Counter C⟩.increment(3)103counter.decrement(2)
  43. self.value ← 8, self.history ← [0, 5, 8]

    pass 2 of 2
    87def increment(self⟨Counter C⟩, amount3=1):88    self.value→ 8 += amount389    self.history→ [0, 5, 8].append(self.value8)  #?record
  44. counter.increment(3)

    101counter.increment(5)102counter⟨Counter C⟩.increment(3)103counter⟨Counter C⟩.decrement(2)104print(f"Value: {counter.value}")
  45. self.value ← 6, self.history ← [0, 5, 8, 6]

    91def decrement(self⟨Counter C⟩, amount2=1):92    self.value→ 6 -= amount293    self.history→ [0, 5, 8, 6].append(self.value6)
  46. counter.decrement(2)

    102counter.increment(3)103counter⟨Counter C⟩.decrement(2)104print(f"Value: {counter.value6}")105print(f"History: {counter.history[0, 5, 8, 6]}")106107counter⟨Counter C⟩.undo()108print(f"After undo: {counter.value}")
    outputValue: 6
    History: [0, 5, 8, 6]
  47. def undo(self): #?undo

    95def undo(self⟨Counter C⟩):  #?undo96    if len(self.history) > 1:97        self.history.pop()
  48. self.history ← [0, 5, 8], self.value ← 8

    95def undo(self):  #?undo96    if len(self.history[0, 5, 8, 6]) > 1:97        self.history→ [0, 5, 8].pop()98        self.value→ 8 = self.history[-1]8
  49. counter.undo()

    107counter⟨Counter C⟩.undo()108print(f"After undo: {counter.value8}")109print(f"History: {counter.history[0, 5, 8]}")110#@help initlist
    outputAfter undo: 8
    History: [0, 5, 8]

Classes can encapsulate complex logic, not just data storage.

Exercise: practical.py

Build a complete class with attributes, methods, and logic