Your code processes shapes. Each shape has an area() method, but Circle and Rectangle calculate area differently. Polymorphism lets you call shape.area() without knowing which specific shape it is - the right method runs automatically.

Duck typing

If it has the right methods, it works.

duck_typing.py
Replay: real traced execution (multi-file project)
# Duck Typing - "If it quacks like a duck..."

class Duck:
    """A real duck."""

    def quack(self):
        return "Quack quack!"

    def walk(self):
        return "Waddle waddle"


class Person:
    """A person who can imitate a duck."""

    def quack(self):
        return "I'm pretending to quack!"

    def walk(self):
        return "Walking on two legs"


class RobotDuck:
    """A mechanical duck toy."""

    def quack(self):
        return "Electronic quack!"

    def walk(self):
        return "Mechanical walking..."


class Dog:
    """A dog cannot quack."""

    def bark(self):
        return "Woof!"

    def walk(self):
        return "Running on four legs"


# Function that uses duck typing
def make_it_quack(duck_like_thing):
    """Works with anything that has quack() method."""
    return duck_like_thing.quack()


def duck_show(duck_like_thing):
    """Demonstrate both quack and walk."""
    print(f"  Quack: {duck_like_thing.quack()}")
    print(f"  Walk: {duck_like_thing.walk()}")


print("=== Duck Typing ===\n")

print("The principle:")
print("'If it walks like a duck and quacks like a duck, it's a duck!'")
print("(Actual type doesn't matter - behavior does)")

# Create objects of different types
real_duck = Duck()
person = Person()
robot = RobotDuck()

print("\n--- Making things quack ---")

# All can quack!
quackers = [real_duck, person, robot]

for obj in quackers:
    print(f"{type(obj).__name__}: {make_it_quack(obj)}")

print("\n--- Full duck demonstration ---")

for obj in quackers:
    print(f"\n{type(obj).__name__}:")
    duck_show(obj)

print("\n--- What about Dog? ---")

dog = Dog()
print(f"Dog can walk: {dog.walk()}")
print("But if we try to make Dog quack...")

try:
    make_it_quack(dog)
except AttributeError as e:
    print(f"Error: {e}")
    print("Dog doesn't have quack() method!")

print("\n=== Duck Typing Rules ===")
print("""
1. No formal interface or inheritance required
2. Object just needs the right methods
3. Type is checked at runtime (not compile time)
4. AttributeError if method missing

Advantages:
- Flexible and dynamic
- Easy to extend
- No type hierarchy needed

Disadvantages:
- Errors only at runtime
- IDE may not catch mistakes
- Need good documentation/naming
""")

  1. real_duck ← ⟨Duck A⟩, person ← ⟨Person B⟩, robot ← ⟨RobotDuck C⟩

    3class Duck: #?duckclass4    """A real duck."""5    6    def quack(self): #?duckquack7        return "Quack quack!"8    9    def walk(self): #?duckwalk10        return "Waddle waddle"111213class Person: #?personclass14    """A person who can imitate a duck."""15    16    def quack(self): #?personquack17        return "I'm pretending to quack!"18    19    def walk(self): #?personwalk20        return "Walking on two legs"212223class RobotDuck: #?robotclass24    """A mechanical duck toy."""25    26    def quack(self): #?robotquack27        return "Electronic quack!"28    29    def walk(self): #?robotwalk30        return "Mechanical walking..."313233class Dog: #?dogclass34    """A dog cannot quack."""35    36    def bark(self): #?dogbark37        return "Woof!"38    39    def walk(self):40        return "Running on four legs"414243# Function that uses duck typing #?ducktypingfunc44def make_it_quack(duck_like_thing): #?makeitquack45    """Works with anything that has quack() method."""46    return duck_like_thing.quack() #?callquack474849def duck_show(duck_like_thing): #?duckshow50    """Demonstrate both quack and walk."""51    print(f"  Quack: {duck_like_thing.quack()}")52    print(f"  Walk: {duck_like_thing.walk()}")535455print("=== Duck Typing ===\n")5657print("The principle:")58print("'If it walks like a duck and quacks like a duck, it's a duck!'")59print("(Actual type doesn't matter - behavior does)")6061# Create objects of different types #?createobjects62real_duck→ ⟨Duck A⟩ = Duck() #?realduck63person→ ⟨Person B⟩ = Person() #?person64robot→ ⟨RobotDuck C⟩ = RobotDuck() #?robot6566print("\n--- Making things quack ---")6768# All can quack! #?allquack69quackers→ [⟨Duck A⟩, ⟨Person B⟩, ⟨RobotDuck C⟩] = [real_duck⟨Duck A⟩, person⟨Person B⟩, robot⟨RobotDuck C⟩] #?quackerslist
    output=== Duck Typing ===
    The principle:
    'If it walks like a duck and quacks like a duck, it's a duck!'
    (Actual type doesn't matter - behavior does)
    
    --- Making things quack ---
  2. for obj in quackers: #?iteratequackers

    pass 1 of 3
    71for obj⟨Duck A⟩ in quackers[⟨Duck A⟩, ⟨Person B⟩, ⟨RobotDuck C⟩]: #?iteratequackers72    print(f"{type(obj⟨Duck A⟩).__name__}: {make_it_quack(obj)}") #?printquack
    All 3 passes — pass 1 is the card above
    passobjself
    1⟨Duck A⟩⟨Duck A⟩
    2⟨Person B⟩⟨Person B⟩
    3⟨RobotDuck C⟩⟨RobotDuck C⟩
  3. def make_it_quack(duck_like_thing): #?makeitquack

    pass 1 of 4
    43# Function that uses duck typing #?ducktypingfunc44def make_it_quack(duck_like_thing⟨Duck A⟩): #?makeitquack45    """Works with anything that has quack() method."""46    return duck_like_thing⟨Duck A⟩.quack() #?callquack
    All 4 passes — pass 1 is the card above
    passduck_like_thingselfe
    1⟨Duck A⟩⟨Duck A⟩
    2⟨Person B⟩⟨Person B⟩
    3⟨RobotDuck C⟩⟨RobotDuck C⟩
    4⟨Dog D⟩'Dog' object has no attribute 'quack'
  4. def quack(self): #?duckquack

    pass 1 of 2
    6def quack(self⟨Duck A⟩): #?duckquack7    return "Quack quack!"
  5. print(f"{type(obj).__name__}: {make_it_quack(obj)}") #?printquack

    71for obj in quackers: #?iteratequackers72    print(f"{type(obj⟨Duck A⟩).__name__}: {make_it_quack(obj)}") #?printquack
    outputDuck: Quack quack!
  6. def quack(self): #?personquack

    pass 1 of 2
    16def quack(self⟨Person B⟩): #?personquack17    return "I'm pretending to quack!"
  7. print(f"{type(obj).__name__}: {make_it_quack(obj)}") #?printquack

    71for obj in quackers: #?iteratequackers72    print(f"{type(obj⟨Person B⟩).__name__}: {make_it_quack(obj)}") #?printquack
    outputPerson: I'm pretending to quack!
  8. def quack(self): #?robotquack

    pass 1 of 2
    26def quack(self⟨RobotDuck C⟩): #?robotquack27    return "Electronic quack!"
  9. print(f"{type(obj).__name__}: {make_it_quack(obj)}") #?printquack

    71for obj in quackers: #?iteratequackers72    print(f"{type(obj⟨RobotDuck C⟩).__name__}: {make_it_quack(obj)}") #?printquack
    outputRobotDuck: Electronic quack!
  10. print(" --- Full duck demonstration ---")

    74print("\n--- Full duck demonstration ---")
    output
    --- Full duck demonstration ---
  11. for obj in quackers:

    pass 1 of 3
    76for obj⟨Duck A⟩ in quackers[⟨Duck A⟩, ⟨Person B⟩, ⟨RobotDuck C⟩]:77    print(f"\n{type(obj⟨Duck A⟩).__name__}:")78    duck_show(obj⟨Duck A⟩) #?callduckshow
    output
    Duck:
    All 3 passes — pass 1 is the card above
    passobjself
    1⟨Duck A⟩⟨Duck A⟩
    2⟨Person B⟩⟨Person B⟩
    3⟨RobotDuck C⟩⟨RobotDuck C⟩
  12. def duck_show(duck_like_thing): #?duckshow

    pass 1 of 3
    49def duck_show(duck_like_thing⟨Duck A⟩): #?duckshow50    """Demonstrate both quack and walk."""51    print(f"  Quack: {duck_like_thing⟨Duck A⟩.quack()}")52    print(f"  Walk: {duck_like_thing.walk()}")
    All 3 passes — pass 1 is the card above
    passduck_like_thingself
    1⟨Duck A⟩⟨Duck A⟩
    2⟨Person B⟩⟨Person B⟩
    3⟨RobotDuck C⟩⟨RobotDuck C⟩
  13. def quack(self): #?duckquack

    pass 2 of 2
    6def quack(self⟨Duck A⟩): #?duckquack7    return "Quack quack!"
  14. print(f" Quack: {duck_like_thing.quack()}")

    50"""Demonstrate both quack and walk."""51print(f"  Quack: {duck_like_thing⟨Duck A⟩.quack()}")52print(f"  Walk: {duck_like_thing⟨Duck A⟩.walk()}")
    output  Quack: Quack quack!
  15. def walk(self): #?duckwalk

    9def walk(self⟨Duck A⟩): #?duckwalk10    return "Waddle waddle"
  16. print(f" Walk: {duck_like_thing.walk()}")

    51print(f"  Quack: {duck_like_thing.quack()}")52print(f"  Walk: {duck_like_thing⟨Duck A⟩.walk()}")
    output  Walk: Waddle waddle
  17. duck_show(obj) #?callduckshow

    77print(f"\n{type(obj).__name__}:")78duck_show(obj⟨Duck A⟩) #?callduckshow
  18. def quack(self): #?personquack

    pass 2 of 2
    16def quack(self⟨Person B⟩): #?personquack17    return "I'm pretending to quack!"
  19. print(f" Quack: {duck_like_thing.quack()}")

    50"""Demonstrate both quack and walk."""51print(f"  Quack: {duck_like_thing⟨Person B⟩.quack()}")52print(f"  Walk: {duck_like_thing⟨Person B⟩.walk()}")
    output  Quack: I'm pretending to quack!
  20. def walk(self): #?personwalk

    19def walk(self⟨Person B⟩): #?personwalk20    return "Walking on two legs"
  21. print(f" Walk: {duck_like_thing.walk()}")

    51print(f"  Quack: {duck_like_thing.quack()}")52print(f"  Walk: {duck_like_thing⟨Person B⟩.walk()}")
    output  Walk: Walking on two legs
  22. duck_show(obj) #?callduckshow

    77print(f"\n{type(obj).__name__}:")78duck_show(obj⟨Person B⟩) #?callduckshow
  23. def quack(self): #?robotquack

    pass 2 of 2
    26def quack(self⟨RobotDuck C⟩): #?robotquack27    return "Electronic quack!"
  24. print(f" Quack: {duck_like_thing.quack()}")

    50"""Demonstrate both quack and walk."""51print(f"  Quack: {duck_like_thing⟨RobotDuck C⟩.quack()}")52print(f"  Walk: {duck_like_thing⟨RobotDuck C⟩.walk()}")
    output  Quack: Electronic quack!
  25. def walk(self): #?robotwalk

    29def walk(self⟨RobotDuck C⟩): #?robotwalk30    return "Mechanical walking..."
  26. print(f" Walk: {duck_like_thing.walk()}")

    51print(f"  Quack: {duck_like_thing.quack()}")52print(f"  Walk: {duck_like_thing⟨RobotDuck C⟩.walk()}")
    output  Walk: Mechanical walking...
  27. duck_show(obj) #?callduckshow

    77print(f"\n{type(obj).__name__}:")78duck_show(obj⟨RobotDuck C⟩) #?callduckshow
  28. dog ← ⟨Dog D⟩

    80print("\n--- What about Dog? ---")8182dog→ ⟨Dog D⟩ = Dog() #?createdog83print(f"Dog can walk: {dog⟨Dog D⟩.walk()}")84print("But if we try to make Dog quack...") #?dogcantquack
    output
    --- What about Dog? ---
  29. def walk(self):

    39def walk(self⟨Dog D⟩):40    return "Running on four legs"
  30. print(f"Dog can walk: {dog.walk()}")

    82dog = Dog() #?createdog83print(f"Dog can walk: {dog⟨Dog D⟩.walk()}")84print("But if we try to make Dog quack...") #?dogcantquack
    outputDog can walk: Running on four legs
    But if we try to make Dog quack...
  31. try:

    86try:87    make_it_quack(dog⟨Dog D⟩) #?tryquackdog88except AttributeError as e: #?attributeerror
  32. except AttributeError as e: #?attributeerror

    87    make_it_quack(dog) #?tryquackdog88except AttributeError as e: #?attributeerror89    print(f"Error: {e'Dog' object has no attribute 'quack'}")90    print("Dog doesn't have quack() method!")
    outputError: 'Dog' object has no attribute 'quack'
    Dog doesn't have quack() method!
  33. print(" === Duck Typing Rules ===")

    92print("\n=== Duck Typing Rules ===")93print("""941. No formal interface or inheritance required952. Object just needs the right methods963. Type is checked at runtime (not compile time)974. AttributeError if method missing9899Advantages:100- Flexible and dynamic101- Easy to extend102- No type hierarchy needed103104Disadvantages:105- Errors only at runtime106- IDE may not catch mistakes107- Need good documentation/naming108""")
    output
    === Duck Typing Rules ===
    
    1. No formal interface or inheritance required
    2. Object just needs the right methods
    3. Type is checked at runtime (not compile time)
    4. AttributeError if method missing
    
    Advantages:
    - Flexible and dynamic
    - Easy to extend
    - No type hierarchy needed
    
    Disadvantages:
    - Errors only at runtime
    - IDE may not catch mistakes
    - Need good documentation/naming

No interface declaration needed. If it has speak(), you can call speak().

duck typing "If it quacks like a duck..." - any object with right methods works.

Method override polymorphism

Same method name, different behavior per class.

method_override_poly.py
Replay: real traced execution (multi-file project)
# Polymorphism Through Method Overriding

class Animal:
    """Base class for animals."""

    def __init__(self, name):
        self.name = name

    def speak(self):
        """Default speak - to be overridden."""
        return f"{self.name} makes a sound"

    def describe(self):
        return f"{self.name} is an animal"


class Dog(Animal):
    """Dog overrides speak."""

    def speak(self):
        return f"{self.name} says: Woof woof!"

    def describe(self):
        return f"{self.name} is a loyal dog"


class Cat(Animal):
    """Cat overrides speak."""

    def speak(self):
        return f"{self.name} says: Meow~"

    def describe(self):
        return f"{self.name} is an independent cat"


class Cow(Animal):
    """Cow overrides speak."""

    def speak(self):
        return f"{self.name} says: Moo!"

    def describe(self):
        return f"{self.name} is a gentle cow"


class Fish(Animal):
    """Fish - some methods make less sense."""

    def speak(self):
        return f"{self.name} says: ... (bubbles)"

    def describe(self):
        return f"{self.name} is a quiet fish"


# Polymorphic function
def animal_concert(animals):
    """Make all animals speak - polymorphic behavior!"""
    print("** Animal Concert **")
    print("-" * 30)
    for animal in animals:
        print(animal.speak())
    print("-" * 30)


def animal_roll_call(animals):
    """Describe all animals."""
    print("\nRoll Call:")
    for animal in animals:
        print(f"  - {animal.describe()}")


print("=== Polymorphism via Method Overriding ===\n")

# Create different animals
animals = [
    Dog("Buddy"),
    Cat("Whiskers"),
    Cow("Bessie"),
    Fish("Nemo"),
    Dog("Max"),
    Cat("Mittens"),
]

# Same method name, different behavior
animal_concert(animals)

animal_roll_call(animals)

# The beauty of polymorphism
print("\n--- Processing uniformly ---")

for animal in animals:
    # All animals have same interface
    print(f"{animal.name}: ", end="")
    print(animal.speak())

# Adding new animal type - no function changes needed!
print("\n--- Adding new animal type ---")

class Duck(Animal):
    def speak(self):
        return f"{self.name} says: Quack quack!"

    def describe(self):
        return f"{self.name} is a happy duck"

# Works immediately with existing functions
duck = Duck("Donald")
print(f"New animal: {duck.describe()}")
print(f"In concert: {duck.speak()}")

# Add to list and process
animals.append(duck)
print(f"\nTotal animals now: {len(animals)}")

print("\n=== Key Points ===")
print("""
1. Parent class defines the method (speak)
2. Each child overrides with specific behavior
3. Functions work with parent type (Animal)
4. Actual behavior depends on runtime type
5. New types work without changing functions

This is the essence of polymorphism:
  - One interface (Animal.speak)
  - Many implementations (Dog, Cat, Cow, etc.)
  - Uniform handling (animal_concert works for all)
""")

  1. """Base class for animals."""

    3class Animal: #?animalclass4    """Base class for animals."""5    6    def __init__(self, name): #?animalinit7        self.name = name8    9    def speak(self): #?animalspeak10        """Default speak - to be overridden."""11        return f"{self.name} makes a sound"12    13    def describe(self): #?animaldescribe14        return f"{self.name} is an animal"151617class Dog(Animal): #?dogclass18    """Dog overrides speak."""19    20    def speak(self): #?dogspeak21        return f"{self.name} says: Woof woof!"22    23    def describe(self):24        return f"{self.name} is a loyal dog"252627class Cat(Animal): #?catclass28    """Cat overrides speak."""29    30    def speak(self): #?catspeak31        return f"{self.name} says: Meow~"32    33    def describe(self):34        return f"{self.name} is an independent cat"353637class Cow(Animal): #?cowclass38    """Cow overrides speak."""39    40    def speak(self): #?cowspeak41        return f"{self.name} says: Moo!"42    43    def describe(self):44        return f"{self.name} is a gentle cow"454647class Fish(Animal): #?fishclass48    """Fish - some methods make less sense."""49    50    def speak(self): #?fishspeak51        return f"{self.name} says: ... (bubbles)"52    53    def describe(self):54        return f"{self.name} is a quiet fish"555657# Polymorphic function #?polyfunc58def animal_concert(animals): #?animalconcert59    """Make all animals speak - polymorphic behavior!"""60    print("** Animal Concert **")61    print("-" * 30)62    for animal in animals: #?iterateanimals63        print(animal.speak()) #?callspeak64    print("-" * 30)656667def animal_roll_call(animals): #?rollcall68    """Describe all animals."""69    print("\nRoll Call:")70    for animal in animals:71        print(f"  - {animal.describe()}") #?calldescribe727374print("=== Polymorphism via Method Overriding ===\n")7576# Create different animals #?createanimals77animals = [ #?animallist78    Dog("Buddy"),79    Cat("Whiskers"),80    Cow("Bessie"),81    Fish("Nemo"),82    Dog("Max"),83    Cat("Mittens"),84]
    output=== Polymorphism via Method Overriding ===
  2. self.name ← Buddy

    pass 1 of 7
    6def __init__(self⟨Dog A⟩, nameBuddy): #?animalinit7    self.name→ Buddy = nameBuddy
    All 7 passes — pass 1 is the card above
    passselfnameself.name
    1⟨Dog A⟩BuddyBuddy
    2⟨Cat B⟩WhiskersWhiskers
    3⟨Cow C⟩BessieBessie
    4⟨Fish D⟩NemoNemo
    5⟨Dog E⟩MaxMax
    6⟨Cat F⟩MittensMittens
    7⟨Duck G⟩DonaldDonald
  3. animals ← [⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]

    76# Create different animals #?createanimals77animals→ [⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩] = [ #?animallist78    Dog("Buddy"),79    Cat("Whiskers"),80    Cow("Bessie"),81    Fish("Nemo"),82    Dog("Max"),83    Cat("Mittens"),84]8586# Same method name, different behavior #?samemethoddiff87animal_concert(animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]) #?callconcert
  4. def animal_concert(animals): #?animalconcert

    57# Polymorphic function #?polyfunc58def animal_concert(animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]): #?animalconcert59    """Make all animals speak - polymorphic behavior!"""60    print("** Animal Concert **")61    print("-" * 30)62    for animal in animals: #?iterateanimals
    output** Animal Concert **
    ------------------------------
  5. for animal in animals: #?iterateanimals

    pass 1 of 6
    61print("-" * 30)62for animal⟨Dog A⟩ in animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]: #?iterateanimals63    print(animal⟨Dog A⟩.speak()) #?callspeak64print("-" * 30)
    All 6 passes — pass 1 is the card above
    passanimalselfself.name
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Cow C⟩⟨Cow C⟩Bessie
    4⟨Fish D⟩⟨Fish D⟩Nemo
    5⟨Dog E⟩
    6⟨Cat F⟩
  6. def speak(self): #?dogspeak

    pass 1 of 4
    20def speak(self⟨Dog A⟩): #?dogspeak21    return f"{self.nameBuddy} says: Woof woof!"
    All 4 passes — pass 1 is the card above
    passselfself.name
    1⟨Dog A⟩Buddy
    2⟨Dog E⟩Max
    3⟨Dog A⟩Buddy
    4⟨Dog E⟩Max
  7. print(animal.speak()) #?callspeak

    62for animal in animals: #?iterateanimals63    print(animal⟨Dog A⟩.speak()) #?callspeak64print("-" * 30)
    outputBuddy says: Woof woof!
  8. def speak(self): #?catspeak

    pass 1 of 4
    30def speak(self⟨Cat B⟩): #?catspeak31    return f"{self.nameWhiskers} says: Meow~"
    All 4 passes — pass 1 is the card above
    passselfself.name
    1⟨Cat B⟩Whiskers
    2⟨Cat F⟩Mittens
    3⟨Cat B⟩Whiskers
    4⟨Cat F⟩Mittens
  9. print(animal.speak()) #?callspeak

    62for animal in animals: #?iterateanimals63    print(animal⟨Cat B⟩.speak()) #?callspeak64print("-" * 30)
    outputWhiskers says: Meow~
  10. def speak(self): #?cowspeak

    pass 1 of 2
    40def speak(self⟨Cow C⟩): #?cowspeak41    return f"{self.nameBessie} says: Moo!"
  11. print(animal.speak()) #?callspeak

    62for animal in animals: #?iterateanimals63    print(animal⟨Cow C⟩.speak()) #?callspeak64print("-" * 30)
    outputBessie says: Moo!
  12. def speak(self): #?fishspeak

    pass 1 of 2
    50def speak(self⟨Fish D⟩): #?fishspeak51    return f"{self.nameNemo} says: ... (bubbles)"
  13. print(animal.speak()) #?callspeak

    62for animal in animals: #?iterateanimals63    print(animal⟨Fish D⟩.speak()) #?callspeak64print("-" * 30)
    outputNemo says: ... (bubbles)
  14. print(animal.speak()) #?callspeak

    62for animal in animals: #?iterateanimals63    print(animal⟨Dog E⟩.speak()) #?callspeak64print("-" * 30)
    outputMax says: Woof woof!
  15. print(animal.speak()) #?callspeak

    62for animal in animals: #?iterateanimals63    print(animal⟨Cat F⟩.speak()) #?callspeak64print("-" * 30)
    outputMittens says: Meow~
  16. print("-" * 30)

    63    print(animal.speak()) #?callspeak64print("-" * 30)
    output------------------------------
  17. animal_concert(animals) #?callconcert

    86# Same method name, different behavior #?samemethoddiff87animal_concert(animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]) #?callconcert8889animal_roll_call(animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]) #?callrollcall
  18. def animal_roll_call(animals): #?rollcall

    67def animal_roll_call(animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]): #?rollcall68    """Describe all animals."""69    print("\nRoll Call:")70    for animal in animals:
    output
    Roll Call:
  19. for animal in animals:

    pass 1 of 6
    69print("\nRoll Call:")70for animal⟨Dog A⟩ in animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]:71    print(f"  - {animal⟨Dog A⟩.describe()}") #?calldescribe
    All 6 passes — pass 1 is the card above
    passanimalselfself.name
    1⟨Dog A⟩⟨Dog A⟩Buddy
    2⟨Cat B⟩⟨Cat B⟩Whiskers
    3⟨Cow C⟩⟨Cow C⟩Bessie
    4⟨Fish D⟩⟨Fish D⟩Nemo
    5⟨Dog E⟩⟨Dog E⟩Max
    6⟨Cat F⟩⟨Cat F⟩Mittens
  20. def describe(self):

    pass 1 of 2
    23def describe(self⟨Dog A⟩):24    return f"{self.nameBuddy} is a loyal dog"
  21. print(f" - {animal.describe()}") #?calldescribe

    70for animal in animals:71    print(f"  - {animal⟨Dog A⟩.describe()}") #?calldescribe
    output  - Buddy is a loyal dog
  22. def describe(self):

    pass 1 of 2
    33def describe(self⟨Cat B⟩):34    return f"{self.nameWhiskers} is an independent cat"
  23. print(f" - {animal.describe()}") #?calldescribe

    70for animal in animals:71    print(f"  - {animal⟨Cat B⟩.describe()}") #?calldescribe
    output  - Whiskers is an independent cat
  24. def describe(self):

    43def describe(self⟨Cow C⟩):44    return f"{self.nameBessie} is a gentle cow"
  25. print(f" - {animal.describe()}") #?calldescribe

    70for animal in animals:71    print(f"  - {animal⟨Cow C⟩.describe()}") #?calldescribe
    output  - Bessie is a gentle cow
  26. def describe(self):

    53def describe(self⟨Fish D⟩):54    return f"{self.nameNemo} is a quiet fish"
  27. print(f" - {animal.describe()}") #?calldescribe

    70for animal in animals:71    print(f"  - {animal⟨Fish D⟩.describe()}") #?calldescribe
    output  - Nemo is a quiet fish
  28. def describe(self):

    pass 2 of 2
    23def describe(self⟨Dog E⟩):24    return f"{self.nameMax} is a loyal dog"
  29. print(f" - {animal.describe()}") #?calldescribe

    70for animal in animals:71    print(f"  - {animal⟨Dog E⟩.describe()}") #?calldescribe
    output  - Max is a loyal dog
  30. def describe(self):

    pass 2 of 2
    33def describe(self⟨Cat F⟩):34    return f"{self.nameMittens} is an independent cat"
  31. print(f" - {animal.describe()}") #?calldescribe

    70for animal in animals:71    print(f"  - {animal⟨Cat F⟩.describe()}") #?calldescribe
    output  - Mittens is an independent cat
  32. animal_roll_call(animals) #?callrollcall

    89animal_roll_call(animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]) #?callrollcall9091# The beauty of polymorphism #?polymorphismbeauty92print("\n--- Processing uniformly ---")
    output
    --- Processing uniformly ---
  33. for animal in animals: #?uniformloop # All animals have same inter…

    pass 1 of 6
    94for animal⟨Dog A⟩ in animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩]: #?uniformloop95    # All animals have same interface #?sameinterface96    print(f"{animal.nameBuddy}: ", end="")97    print(animal⟨Dog A⟩.speak()) #?polymorphiccall
    outputBuddy: 
    All 6 passes — pass 1 is the card above
    passanimalanimal.nameselfself.name
    1⟨Dog A⟩Buddy
    2⟨Cat B⟩Whiskers
    3⟨Cow C⟩Bessie⟨Cow C⟩Bessie
    4⟨Fish D⟩Nemo⟨Fish D⟩Nemo
    5⟨Dog E⟩Max
    6⟨Cat F⟩Mittens
  34. print(animal.speak()) #?polymorphiccall

    96print(f"{animal.name}: ", end="")97print(animal⟨Dog A⟩.speak()) #?polymorphiccall
    outputBuddy says: Woof woof!
  35. print(animal.speak()) #?polymorphiccall

    96print(f"{animal.name}: ", end="")97print(animal⟨Cat B⟩.speak()) #?polymorphiccall
    outputWhiskers says: Meow~
  36. def speak(self): #?cowspeak

    pass 2 of 2
    40def speak(self⟨Cow C⟩): #?cowspeak41    return f"{self.nameBessie} says: Moo!"
  37. print(animal.speak()) #?polymorphiccall

    96print(f"{animal.name}: ", end="")97print(animal⟨Cow C⟩.speak()) #?polymorphiccall
    outputBessie says: Moo!
  38. def speak(self): #?fishspeak

    pass 2 of 2
    50def speak(self⟨Fish D⟩): #?fishspeak51    return f"{self.nameNemo} says: ... (bubbles)"
  39. print(animal.speak()) #?polymorphiccall

    96print(f"{animal.name}: ", end="")97print(animal⟨Fish D⟩.speak()) #?polymorphiccall
    outputNemo says: ... (bubbles)
  40. print(animal.speak()) #?polymorphiccall

    96print(f"{animal.name}: ", end="")97print(animal⟨Dog E⟩.speak()) #?polymorphiccall
    outputMax says: Woof woof!
  41. print(animal.speak()) #?polymorphiccall

    96print(f"{animal.name}: ", end="")97print(animal⟨Cat F⟩.speak()) #?polymorphiccall
    outputMittens says: Meow~
  42. print(" --- Adding new animal type ---")

    99# Adding new animal type - no function changes needed! #?extensible100print("\n--- Adding new animal type ---")101102class Duck(Animal): #?duckclass103    def speak(self): #?duckspeak104        return f"{self.name} says: Quack quack!"105    106    def describe(self):107        return f"{self.name} is a happy duck"108109# Works immediately with existing functions #?workimmediately110duck = Duck("Donald") #?createduck111print(f"New animal: {duck.describe()}")
    output
    --- Adding new animal type ---
  43. duck ← ⟨Duck G⟩

    109# Works immediately with existing functions #?workimmediately110duck→ ⟨Duck G⟩ = Duck("Donald") #?createduck111print(f"New animal: {duck⟨Duck G⟩.describe()}")112print(f"In concert: {duck.speak()}")
  44. def describe(self):

    106def describe(self⟨Duck G⟩):107    return f"{self.nameDonald} is a happy duck"
  45. print(f"New animal: {duck.describe()}")

    110duck = Duck("Donald") #?createduck111print(f"New animal: {duck⟨Duck G⟩.describe()}")112print(f"In concert: {duck⟨Duck G⟩.speak()}")
    outputNew animal: Donald is a happy duck
  46. def speak(self): #?duckspeak

    102class Duck(Animal): #?duckclass103    def speak(self⟨Duck G⟩): #?duckspeak104        return f"{self.nameDonald} says: Quack quack!"
  47. animals ← [⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩, ⟨Duck G⟩]

    111print(f"New animal: {duck.describe()}")112print(f"In concert: {duck⟨Duck G⟩.speak()}")113114# Add to list and process #?addtolist115animals→ [⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩, ⟨Duck G⟩].append(duck⟨Duck G⟩)116print(f"\nTotal animals now: {len(animals[⟨Dog A⟩, ⟨Cat B⟩, ⟨Cow C⟩, ⟨Fish D⟩, ⟨Dog E⟩, ⟨Cat F⟩, ⟨Duck G⟩])}")117118print("\n=== Key Points ===")119print("""1201. Parent class defines the method (speak)1212. Each child overrides with specific behavior1223. Functions work with parent type (Animal)1234. Actual behavior depends on runtime type1245. New types work without changing functions125126This is the essence of polymorphism:127  - One interface (Animal.speak)128  - Many implementations (Dog, Cat, Cow, etc.)129  - Uniform handling (animal_concert works for all)130""")
    outputIn concert: Donald says: Quack quack!
    
    Total animals now: 7
    
    === Key Points ===
    
    1. Parent class defines the method (speak)
    2. Each child overrides with specific behavior
    3. Functions work with parent type (Animal)
    4. Actual behavior depends on runtime type
    5. New types work without changing functions
    
    This is the essence of polymorphism:
      - One interface (Animal.speak)
      - Many implementations (Dog, Cat, Cow, etc.)
      - Uniform handling (animal_concert works for all)

Parent defines method. Each child overrides with its own implementation.

Common interface

Design classes to share method names.

common_interface.py
# Designing Classes with Common Interface

class Drawable:
    """
    Classes that can be drawn share this interface.
    In Python, this is just a convention - no formal interface.
    """

    def draw(self):
        """Draw the object - override this!"""
        raise NotImplementedError("Subclasses must implement draw()")

    def get_bounds(self):
        """Return bounding box - override this!"""
        raise NotImplementedError("Subclasses must implement get_bounds()")


class Circle:
    """Circle implements Drawable interface."""

    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius

    def draw(self):
        return f"Drawing circle at ({self.x}, {self.y}) with radius {self.radius}"

    def get_bounds(self):
        return (self.x - self.radius, self.y - self.radius,
                self.x + self.radius, self.y + self.radius)


class Rectangle:
    """Rectangle implements Drawable interface."""

    def __init__(self, x, y, width, height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height

    def draw(self):
        return f"Drawing rectangle at ({self.x}, {self.y}), size {self.width}x{self.height}"

    def get_bounds(self):
        return (self.x, self.y, self.x + self.width, self.y + self.height)


class Line:
    """Line implements Drawable interface."""

    def __init__(self, x1, y1, x2, y2):
        self.x1, self.y1 = x1, y1
        self.x2, self.y2 = x2, y2

    def draw(self):
        return f"Drawing line from ({self.x1}, {self.y1}) to ({self.x2}, {self.y2})"

    def get_bounds(self):
        return (min(self.x1, self.x2), min(self.y1, self.y2),
                max(self.x1, self.x2), max(self.y1, self.y2))


class Text:
    """Text implements Drawable interface."""

    def __init__(self, x, y, content, font_size=12):
        self.x = x
        self.y = y
        self.content = content
        self.font_size = font_size

    def draw(self):
        return f"Drawing text '{self.content}' at ({self.x}, {self.y}), size {self.font_size}"

    def get_bounds(self):
        # Approximate bounds based on content length
        width = len(self.content) * self.font_size * 0.6
        height = self.font_size
        return (self.x, self.y, self.x + width, self.y + height)


# Canvas that uses the common interface
class Canvas:
    """Canvas that holds and renders drawable objects."""

    def __init__(self, name):
        self.name = name
        self.objects = []

    def add(self, drawable):
        """Add any object with draw() method."""
        self.objects.append(drawable)

    def render_all(self):
        """Render all objects - polymorphic!"""
        print(f"\n🖼️ Rendering {self.name}:")
        print("-" * 40)
        for obj in self.objects:
            print(f"  {obj.draw()}")
        print("-" * 40)

    def get_total_bounds(self):
        """Calculate bounding box for all objects."""
        if not self.objects:
            return (0, 0, 0, 0)

        bounds_list = [obj.get_bounds() for obj in self.objects]
        min_x = min(b[0] for b in bounds_list)
        min_y = min(b[1] for b in bounds_list)
        max_x = max(b[2] for b in bounds_list)
        max_y = max(b[3] for b in bounds_list)

        return (min_x, min_y, max_x, max_y)


print("=== Common Interface Design ===\n")

# Create drawable objects
circle = Circle(100, 100, 50)
rect = Rectangle(200, 50, 80, 60)
line = Line(0, 0, 150, 150)
text = Text(50, 200, "Hello World", 14)

# Create canvas and add objects
canvas = Canvas("My Drawing")
canvas.add(circle)
canvas.add(rect)
canvas.add(line)
canvas.add(text)

# Render - polymorphism in action!
canvas.render_all()

# Get bounds - also polymorphic
total_bounds = canvas.get_total_bounds()
print(f"\nTotal canvas bounds: {total_bounds}")

# Demonstrate individual bounds
print("\nIndividual bounds:")
for obj in canvas.objects:
    print(f"  {type(obj).__name__}: {obj.get_bounds()}")

# Add more objects dynamically
print("\n--- Adding more objects ---")
canvas.add(Circle(300, 300, 25))
canvas.add(Text(10, 10, "New!", 20))

canvas.render_all()

print("\n=== Interface Design Best Practices ===")
print("""
1. Define clear method signatures
   - draw() for all drawable objects
   - get_bounds() for spatial info

2. Document the expected interface
   - Use docstrings
   - Consider type hints

3. Use NotImplementedError for base class
   - Signals "must override"
   - Fails fast if forgotten

4. Keep interface minimal
   - Only essential methods
   - Easy to implement

5. Test with duck typing
   - If it has draw() and get_bounds()
   - It works with Canvas!
""")

All shapes have area() and perimeter(). Code works with any shape.

interface Set of methods a class provides. In Python, implicit through duck typing.

Polymorphic functions

Write functions that work with any compatible object.

speakers
polymorphic_functions.py
Replay: real traced execution (multi-file project)
# Polymorphic Functions

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

    def speak(self):
        return "Woof!"

    def __len__(self):
        return len(self.name)


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

    def speak(self):
        return "Meow!"

    def __len__(self):
        return len(self.name)


class Robot:
    def __init__(self, model):
        self.model = model

    def speak(self):
        return "Beep boop!"

    def __len__(self):
        return len(self.model)


# Polymorphic function - works with any object that has speak()
def make_speak(speaker):
    """
    Works with any object that has a speak() method.
    This is polymorphism via duck typing.
    """
    return speaker.speak()


# Function with type-specific logic
def describe_speaker(speaker):
    """
    Polymorphic function that also handles type differences.
    """
    sound = speaker.speak()

    # Can use hasattr to check for attributes
    if hasattr(speaker, 'name'):
        identifier = speaker.name
    elif hasattr(speaker, 'model'):
        identifier = speaker.model
    else:
        identifier = "Unknown"

    return f"{identifier} says: {sound}"


# Function using len() - built-in polymorphism
def show_length(obj):
    """
    len() is polymorphic - works with many types.
    Requires __len__ method.
    """
    return f"Length of {type(obj).__name__}: {len(obj)}"


# Function accepting multiple types with shared behavior
def make_chorus(speakers, times=2):
    """
    Make all speakers speak multiple times.
    Works with any list of objects with speak() method.
    """
    print("\n** Chorus Time! **")
    for _ in range(times):
        for speaker in speakers:
            print(f"  {make_speak(speaker)}")
        print("---")


# Higher-order polymorphic function
def process_speakers(speakers, processor):
    """
    Apply any processor function to speakers.
    Both the speakers and processor are polymorphic!
    """
    results = []
    for speaker in speakers:
        result = processor(speaker)
        results.append(result)
    return results


print("=== Polymorphic Functions ===\n")

# Create different objects
dog = Dog("Buddy")
cat = Cat("Whiskers")
robot = Robot("R2D2")

speakers = [dog, cat, robot]

# Basic polymorphic function
print("--- make_speak (basic polymorphism) ---")
for s in speakers:
    print(f"  {make_speak(s)}")

# Polymorphic with type handling
print("\n--- describe_speaker (with type handling) ---")
for s in speakers:
    print(f"  {describe_speaker(s)}")

# Built-in len() polymorphism
print("\n--- show_length (built-in len polymorphism) ---")
for s in speakers:
    print(f"  {show_length(s)}")

# Also works with built-in types!
print("\n  Also works with built-in types:")
print(f"  {show_length('Hello')}")
print(f"  {show_length([1, 2, 3])}")
print(f"  {show_length({'a': 1, 'b': 2})}")

# Chorus function
make_chorus(speakers)

# Higher-order function
print("\n--- process_speakers (higher-order) ---")

# Different processors
def loud_speak(s):
    return s.speak().upper() + "!!"

def count_speak(s):
    return f"{s.speak()} ({len(s.speak())} chars)"

print("Loud processor:")
results = process_speakers(speakers, loud_speak)
for r in results:
    print(f"  {r}")

print("\nCount processor:")
results = process_speakers(speakers, count_speak)
for r in results:
    print(f"  {r}")

print("\n=== Polymorphic Function Benefits ===")
print("""
1. Reusable: One function works with many types
2. Flexible: Easy to add new types
3. Clean: No type-checking spaghetti
4. Pythonic: Embraces duck typing

Best practices:
- Document expected interface
- Use meaningful parameter names
- Handle missing methods gracefully (hasattr)
- Consider type hints for documentation
""")

# Polymorphic Functions

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

    def speak(self):
        return "Woof!"

    def __len__(self):
        return len(self.name)


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

    def speak(self):
        return "Meow!"

    def __len__(self):
        return len(self.name)


class Robot:
    def __init__(self, model):
        self.model = model

    def speak(self):
        return "Beep boop!"

    def __len__(self):
        return len(self.model)


# Polymorphic function - works with any object that has speak()
def make_speak(speaker):
    """
    Works with any object that has a speak() method.
    This is polymorphism via duck typing.
    """
    return speaker.speak()


# Function with type-specific logic
def describe_speaker(speaker):
    """
    Polymorphic function that also handles type differences.
    """
    sound = speaker.speak()

    # Can use hasattr to check for attributes
    if hasattr(speaker, 'name'):
        identifier = speaker.name
    elif hasattr(speaker, 'model'):
        identifier = speaker.model
    else:
        identifier = "Unknown"

    return f"{identifier} says: {sound}"


# Function using len() - built-in polymorphism
def show_length(obj):
    """
    len() is polymorphic - works with many types.
    Requires __len__ method.
    """
    return f"Length of {type(obj).__name__}: {len(obj)}"


# Function accepting multiple types with shared behavior
def make_chorus(speakers, times=2):
    """
    Make all speakers speak multiple times.
    Works with any list of objects with speak() method.
    """
    print("\n** Chorus Time! **")
    for _ in range(times):
        for speaker in speakers:
            print(f"  {make_speak(speaker)}")
        print("---")


# Higher-order polymorphic function
def process_speakers(speakers, processor):
    """
    Apply any processor function to speakers.
    Both the speakers and processor are polymorphic!
    """
    results = []
    for speaker in speakers:
        result = processor(speaker)
        results.append(result)
    return results


print("=== Polymorphic Functions ===\n")

# Create different objects
dog = Dog("Buddy")
cat = Cat("Whiskers")
robot = Robot("R2D2")

speakers = [dog, robot]

# Basic polymorphic function
print("--- make_speak (basic polymorphism) ---")
for s in speakers:
    print(f"  {make_speak(s)}")

# Polymorphic with type handling
print("\n--- describe_speaker (with type handling) ---")
for s in speakers:
    print(f"  {describe_speaker(s)}")

# Built-in len() polymorphism
print("\n--- show_length (built-in len polymorphism) ---")
for s in speakers:
    print(f"  {show_length(s)}")

# Also works with built-in types!
print("\n  Also works with built-in types:")
print(f"  {show_length('Hello')}")
print(f"  {show_length([1, 2, 3])}")
print(f"  {show_length({'a': 1, 'b': 2})}")

# Chorus function
make_chorus(speakers)

# Higher-order function
print("\n--- process_speakers (higher-order) ---")

# Different processors
def loud_speak(s):
    return s.speak().upper() + "!!"

def count_speak(s):
    return f"{s.speak()} ({len(s.speak())} chars)"

print("Loud processor:")
results = process_speakers(speakers, loud_speak)
for r in results:
    print(f"  {r}")

print("\nCount processor:")
results = process_speakers(speakers, count_speak)
for r in results:
    print(f"  {r}")

print("\n=== Polymorphic Function Benefits ===")
print("""
1. Reusable: One function works with many types
2. Flexible: Easy to add new types
3. Clean: No type-checking spaghetti
4. Pythonic: Embraces duck typing

Best practices:
- Document expected interface
- Use meaningful parameter names
- Handle missing methods gracefully (hasattr)
- Consider type hints for documentation
""")

# Polymorphic Functions

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

    def speak(self):
        return "Woof!"

    def __len__(self):
        return len(self.name)


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

    def speak(self):
        return "Meow!"

    def __len__(self):
        return len(self.name)


class Robot:
    def __init__(self, model):
        self.model = model

    def speak(self):
        return "Beep boop!"

    def __len__(self):
        return len(self.model)


# Polymorphic function - works with any object that has speak()
def make_speak(speaker):
    """
    Works with any object that has a speak() method.
    This is polymorphism via duck typing.
    """
    return speaker.speak()


# Function with type-specific logic
def describe_speaker(speaker):
    """
    Polymorphic function that also handles type differences.
    """
    sound = speaker.speak()

    # Can use hasattr to check for attributes
    if hasattr(speaker, 'name'):
        identifier = speaker.name
    elif hasattr(speaker, 'model'):
        identifier = speaker.model
    else:
        identifier = "Unknown"

    return f"{identifier} says: {sound}"


# Function using len() - built-in polymorphism
def show_length(obj):
    """
    len() is polymorphic - works with many types.
    Requires __len__ method.
    """
    return f"Length of {type(obj).__name__}: {len(obj)}"


# Function accepting multiple types with shared behavior
def make_chorus(speakers, times=2):
    """
    Make all speakers speak multiple times.
    Works with any list of objects with speak() method.
    """
    print("\n** Chorus Time! **")
    for _ in range(times):
        for speaker in speakers:
            print(f"  {make_speak(speaker)}")
        print("---")


# Higher-order polymorphic function
def process_speakers(speakers, processor):
    """
    Apply any processor function to speakers.
    Both the speakers and processor are polymorphic!
    """
    results = []
    for speaker in speakers:
        result = processor(speaker)
        results.append(result)
    return results


print("=== Polymorphic Functions ===\n")

# Create different objects
dog = Dog("Buddy")
cat = Cat("Whiskers")
robot = Robot("R2D2")

speakers = [cat]

# Basic polymorphic function
print("--- make_speak (basic polymorphism) ---")
for s in speakers:
    print(f"  {make_speak(s)}")

# Polymorphic with type handling
print("\n--- describe_speaker (with type handling) ---")
for s in speakers:
    print(f"  {describe_speaker(s)}")

# Built-in len() polymorphism
print("\n--- show_length (built-in len polymorphism) ---")
for s in speakers:
    print(f"  {show_length(s)}")

# Also works with built-in types!
print("\n  Also works with built-in types:")
print(f"  {show_length('Hello')}")
print(f"  {show_length([1, 2, 3])}")
print(f"  {show_length({'a': 1, 'b': 2})}")

# Chorus function
make_chorus(speakers)

# Higher-order function
print("\n--- process_speakers (higher-order) ---")

# Different processors
def loud_speak(s):
    return s.speak().upper() + "!!"

def count_speak(s):
    return f"{s.speak()} ({len(s.speak())} chars)"

print("Loud processor:")
results = process_speakers(speakers, loud_speak)
for r in results:
    print(f"  {r}")

print("\nCount processor:")
results = process_speakers(speakers, count_speak)
for r in results:
    print(f"  {r}")

print("\n=== Polymorphic Function Benefits ===")
print("""
1. Reusable: One function works with many types
2. Flexible: Easy to add new types
3. Clean: No type-checking spaghetti
4. Pythonic: Embraces duck typing

Best practices:
- Document expected interface
- Use meaningful parameter names
- Handle missing methods gracefully (hasattr)
- Consider type hints for documentation
""")

  1. print("=== Polymorphic Functions === ")

    98print("=== Polymorphic Functions ===\n")99100# Create different objects #?createobjects101dog = Dog("Buddy") #?createdog102cat = Cat("Whiskers") #?createcat
    output=== Polymorphic Functions ===
  2. self.name ← Buddy

    3class Dog: #?dogclass4    def __init__(self⟨Dog A⟩, nameBuddy):5        self.name→ Buddy = nameBuddy
  3. dog ← ⟨Dog A⟩

    100# Create different objects #?createobjects101dog→ ⟨Dog A⟩ = Dog("Buddy") #?createdog102cat = Cat("Whiskers") #?createcat103robot = Robot("R2D2") #?createrobot
  4. self.name ← Whiskers

    14class Cat: #?catclass15    def __init__(self⟨Cat B⟩, nameWhiskers):16        self.name→ Whiskers = nameWhiskers
  5. cat ← ⟨Cat B⟩

    101dog = Dog("Buddy") #?createdog102cat→ ⟨Cat B⟩ = Cat("Whiskers") #?createcat103robot = Robot("R2D2") #?createrobot
  6. self.model ← R2D2

    25class Robot: #?robotclass26    def __init__(self⟨Robot C⟩, modelR2D2):27        self.model→ R2D2 = modelR2D2
  7. robot ← ⟨Robot C⟩, speakers ← [⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]

    102cat = Cat("Whiskers") #?createcat103robot→ ⟨Robot C⟩ = Robot("R2D2") #?createrobot104105speakers→ [⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩] = [dog⟨Dog A⟩, cat⟨Cat B⟩, robot⟨Robot C⟩] #?speakerslist106#@speakers=[dog, robot], [cat], [dog, cat, robot]107108# Basic polymorphic function #?basicpoly109print("--- make_speak (basic polymorphism) ---")110for s in speakers:
    output--- make_speak (basic polymorphism) ---
  8. for s in speakers:

    pass 1 of 3
    109print("--- make_speak (basic polymorphism) ---")110for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]:111    print(f"  {make_speak(s⟨Dog A⟩)}") #?callmakespeak
    All 3 passes — pass 1 is the card above
    passs
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Robot C⟩
  9. def make_speak(speaker): #?makespeak

    pass 1 of 9
    36# Polymorphic function - works with any object that has speak() #?polymorphicfunc37def make_speak(speaker⟨Dog A⟩): #?makespeak38    """39    Works with any object that has a speak() method.40    This is polymorphism via duck typing.41    """42    return speaker⟨Dog A⟩.speak() #?callspeak
    All 9 passes — pass 1 is the card above
    passspeaker
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Robot C⟩
    4⟨Dog A⟩
    5⟨Cat B⟩
    6⟨Robot C⟩
    7⟨Dog A⟩
    8⟨Cat B⟩
    9⟨Robot C⟩
  10. def speak(self):

    pass 1 of 7
    7def speak(self⟨Dog A⟩):8    return "Woof!"
  11. print(f" {make_speak(s)}") #?callmakespeak

    110for s in speakers:111    print(f"  {make_speak(s⟨Dog A⟩)}") #?callmakespeak
    output  Woof!
  12. def speak(self):

    pass 1 of 7
    18def speak(self⟨Cat B⟩):19    return "Meow!"
  13. print(f" {make_speak(s)}") #?callmakespeak

    110for s in speakers:111    print(f"  {make_speak(s⟨Cat B⟩)}") #?callmakespeak
    output  Meow!
  14. def speak(self):

    pass 1 of 7
    29def speak(self⟨Robot C⟩):30    return "Beep boop!"
  15. print(f" {make_speak(s)}") #?callmakespeak

    110for s in speakers:111    print(f"  {make_speak(s⟨Robot C⟩)}") #?callmakespeak
    output  Beep boop!
  16. print(" --- describe_speaker (with type handling) ---")

    113# Polymorphic with type handling #?polytypehandle114print("\n--- describe_speaker (with type handling) ---")115for s in speakers:
    output
    --- describe_speaker (with type handling) ---
  17. for s in speakers:

    pass 1 of 3
    114print("\n--- describe_speaker (with type handling) ---")115for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]:116    print(f"  {describe_speaker(s⟨Dog A⟩)}") #?calldescribe
    All 3 passes — pass 1 is the card above
    passs
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Robot C⟩
  18. def describe_speaker(speaker): #?describespeaker

    pass 1 of 3
    45# Function with type-specific logic #?typespecific46def describe_speaker(speaker⟨Dog A⟩): #?describespeaker47    """48    Polymorphic function that also handles type differences.49    """50    sound = speaker⟨Dog A⟩.speak()
    All 3 passes — pass 1 is the card above
    passspeaker
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Robot C⟩
  19. sound ← Woof!

    49"""50sound→ Woof! = speaker⟨Dog A⟩.speak()
  20. identifier ← Buddy

    pass 1 of 2
    52# Can use hasattr to check for attributes #?hasattr53if hasattr(speaker⟨Dog A⟩, 'name'): #?checkname54    identifier→ Buddy = speaker.nameBuddy55elif hasattr(speaker, 'model'): #?checkmodel
  21. return f"{identifier} says: {sound}"

    60return f"{identifierBuddy} says: {soundWoof!}"
  22. print(f" {describe_speaker(s)}") #?calldescribe

    115for s in speakers:116    print(f"  {describe_speaker(s⟨Dog A⟩)}") #?calldescribe
    output  Buddy says: Woof!
  23. sound ← Meow!

    49"""50sound→ Meow! = speaker⟨Cat B⟩.speak()
  24. identifier ← Whiskers

    pass 2 of 2
    52# Can use hasattr to check for attributes #?hasattr53if hasattr(speaker⟨Cat B⟩, 'name'): #?checkname54    identifier→ Whiskers = speaker.nameWhiskers55elif hasattr(speaker, 'model'): #?checkmodel
  25. return f"{identifier} says: {sound}"

    60return f"{identifierWhiskers} says: {soundMeow!}"
  26. print(f" {describe_speaker(s)}") #?calldescribe

    115for s in speakers:116    print(f"  {describe_speaker(s⟨Cat B⟩)}") #?calldescribe
    output  Whiskers says: Meow!
  27. sound ← Beep boop!

    49"""50sound→ Beep boop! = speaker⟨Robot C⟩.speak()
  28. identifier ← R2D2

    54    identifier = speaker.name55elif hasattr(speaker⟨Robot C⟩, 'model'): #?checkmodel56    identifier→ R2D2 = speaker.modelR2D257else:
  29. return f"{identifier} says: {sound}"

    60return f"{identifierR2D2} says: {soundBeep boop!}"
  30. print(f" {describe_speaker(s)}") #?calldescribe

    115for s in speakers:116    print(f"  {describe_speaker(s⟨Robot C⟩)}") #?calldescribe
    output  R2D2 says: Beep boop!
  31. print(" --- show_length (built-in len polymorphism) ---")

    118# Built-in len() polymorphism #?builtinpoly119print("\n--- show_length (built-in len polymorphism) ---")120for s in speakers:
    output
    --- show_length (built-in len polymorphism) ---
  32. for s in speakers:

    pass 1 of 3
    119print("\n--- show_length (built-in len polymorphism) ---")120for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]:121    print(f"  {show_length(s⟨Dog A⟩)}") #?callshowlen
    All 3 passes — pass 1 is the card above
    passsselfself.nameself.model
    1⟨Dog A⟩⟨Dog A⟩Buddy
    2⟨Cat B⟩⟨Cat B⟩Whiskers
    3⟨Robot C⟩⟨Robot C⟩R2D2
  33. def show_length(obj): #?showlength

    pass 1 of 6
    63# Function using len() - built-in polymorphism #?lenfunc64def show_length(obj⟨Dog A⟩): #?showlength65    """66    len() is polymorphic - works with many types.67    Requires __len__ method.68    """69    return f"Length of {type(obj⟨Dog A⟩).__name__}: {len(obj)}" #?calllen
    All 6 passes — pass 1 is the card above
    passobjselfself.nameself.model
    1⟨Dog A⟩⟨Dog A⟩Buddy
    2⟨Cat B⟩⟨Cat B⟩Whiskers
    3⟨Robot C⟩⟨Robot C⟩R2D2
    4Hello
    5[1, 2, 3]
    6{'a': 1, 'b': 2}
  34. def __len__(self): #?doglen

    10def __len__(self⟨Dog A⟩): #?doglen11    return len(self.nameBuddy)
  35. print(f" {show_length(s)}") #?callshowlen

    120for s in speakers:121    print(f"  {show_length(s⟨Dog A⟩)}") #?callshowlen
    output  Length of Dog: 5
  36. def __len__(self):

    21def __len__(self⟨Cat B⟩):22    return len(self.nameWhiskers)
  37. print(f" {show_length(s)}") #?callshowlen

    120for s in speakers:121    print(f"  {show_length(s⟨Cat B⟩)}") #?callshowlen
    output  Length of Cat: 8
  38. def __len__(self):

    32def __len__(self⟨Robot C⟩):33    return len(self.modelR2D2)
  39. print(f" {show_length(s)}") #?callshowlen

    120for s in speakers:121    print(f"  {show_length(s⟨Robot C⟩)}") #?callshowlen
    output  Length of Robot: 4
  40. print(" Also works with built-in types:")

    123# Also works with built-in types! #?builtintypes124print("\n  Also works with built-in types:")125print(f"  {show_length('Hello')}") #?stringlen126print(f"  {show_length([1, 2, 3])}") #?listlen
    output
      Also works with built-in types:
  41. print(f" {show_length('Hello')}") #?stringlen

    124print("\n  Also works with built-in types:")125print(f"  {show_length('Hello')}") #?stringlen126print(f"  {show_length([1, 2, 3])}") #?listlen127print(f"  {show_length({'a': 1, 'b': 2})}") #?dictlen
    output  Length of str: 5
  42. print(f" {show_length([1, 2, 3])}") #?listlen

    125print(f"  {show_length('Hello')}") #?stringlen126print(f"  {show_length([1, 2, 3])}") #?listlen127print(f"  {show_length({'a': 1, 'b': 2})}") #?dictlen
    output  Length of list: 3
  43. make_chorus(speakers) #?callchorus

    126print(f"  {show_length([1, 2, 3])}") #?listlen127print(f"  {show_length({'a': 1, 'b': 2})}") #?dictlen128129# Chorus function #?chorusdemo130make_chorus(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]) #?callchorus
    output  Length of dict: 2
  44. def make_chorus(speakers, times=2): #?makechorus

    72# Function accepting multiple types with shared behavior #?sharedbehavior73def make_chorus(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩], times2=2): #?makechorus74    """75    Make all speakers speak multiple times.76    Works with any list of objects with speak() method.77    """78    print("\n** Chorus Time! **")79    for _ in range(times): #?repeattimes
    output
    ** Chorus Time! **
  45. for _ in range(times): #?repeattimes

    pass 1 of 2
    78print("\n** Chorus Time! **")79for _0 in range(times2): #?repeattimes80    for speaker in speakers: #?iteratespeakers81        print(f"  {make_speak(speaker)}") #?delegatespeak
  46. for speaker in speakers: #?iteratespeakers

    pass 1 of 6
    79for _ in range(times): #?repeattimes80    for speaker⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]: #?iteratespeakers81        print(f"  {make_speak(speaker⟨Dog A⟩)}") #?delegatespeak82    print("---")
    All 6 passes — pass 1 is the card above
    passspeaker
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Robot C⟩
    4⟨Dog A⟩
    5⟨Cat B⟩
    6⟨Robot C⟩
  47. print(f" {make_speak(speaker)}") #?delegatespeak

    80for speaker in speakers: #?iteratespeakers81    print(f"  {make_speak(speaker⟨Dog A⟩)}") #?delegatespeak82print("---")
    output  Woof!
  48. print(f" {make_speak(speaker)}") #?delegatespeak

    80for speaker in speakers: #?iteratespeakers81    print(f"  {make_speak(speaker⟨Cat B⟩)}") #?delegatespeak82print("---")
    output  Meow!
  49. print(f" {make_speak(speaker)}") #?delegatespeak

    80for speaker in speakers: #?iteratespeakers81    print(f"  {make_speak(speaker⟨Robot C⟩)}") #?delegatespeak82print("---")
    output  Beep boop!
  50. print("---")

    81    print(f"  {make_speak(speaker)}") #?delegatespeak82print("---")
    output---
  51. for _ in range(times): #?repeattimes

    pass 2 of 2
    78print("\n** Chorus Time! **")79for _1 in range(times2): #?repeattimes80    for speaker in speakers: #?iteratespeakers81        print(f"  {make_speak(speaker)}") #?delegatespeak
  52. print(f" {make_speak(speaker)}") #?delegatespeak

    80for speaker in speakers: #?iteratespeakers81    print(f"  {make_speak(speaker⟨Dog A⟩)}") #?delegatespeak82print("---")
    output  Woof!
  53. print(f" {make_speak(speaker)}") #?delegatespeak

    80for speaker in speakers: #?iteratespeakers81    print(f"  {make_speak(speaker⟨Cat B⟩)}") #?delegatespeak82print("---")
    output  Meow!
  54. print(f" {make_speak(speaker)}") #?delegatespeak

    80for speaker in speakers: #?iteratespeakers81    print(f"  {make_speak(speaker⟨Robot C⟩)}") #?delegatespeak82print("---")
    output  Beep boop!
  55. print("---")

    81    print(f"  {make_speak(speaker)}") #?delegatespeak82print("---")
    output---
  56. make_chorus(speakers) #?callchorus

    129# Chorus function #?chorusdemo130make_chorus(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]) #?callchorus131132# Higher-order function #?highorderdemo133print("\n--- process_speakers (higher-order) ---")134135# Different processors #?processors136def loud_speak(s): #?loudspeak137    return s.speak().upper() + "!!"138139def count_speak(s): #?countspeak140    return f"{s.speak()} ({len(s.speak())} chars)"141142print("Loud processor:")143results = process_speakers(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩], loud_speak⟨function loud_speak D⟩) #?processloud144for r in results:
    output
    --- process_speakers (higher-order) ---
    Loud processor:
  57. results ← []

    pass 1 of 2
    85# Higher-order polymorphic function #?higherorder86def process_speakers(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩], processor⟨function loud_speak D⟩): #?processspeakers87    """88    Apply any processor function to speakers.89    Both the speakers and processor are polymorphic!90    """91    results→ [] = []92    for speaker in speakers:
  58. for speaker in speakers:

    pass 1 of 6
    91results = []92for speaker⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]:93    result = processor(speaker⟨Dog A⟩) #?callprocessor94    results.append(result)
    All 6 passes — pass 1 is the card above
    passspeaker
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Robot C⟩
    4⟨Dog A⟩
    5⟨Cat B⟩
    6⟨Robot C⟩
  59. def loud_speak(s): #?loudspeak

    pass 1 of 3
    135# Different processors #?processors136def loud_speak(s⟨Dog A⟩): #?loudspeak137    return s⟨Dog A⟩.speak().upper() + "!!"
    All 3 passes — pass 1 is the card above
    passs
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Robot C⟩
  60. result ← WOOF!!!, results ← ['WOOF!!!']

    92for speaker in speakers:93    result→ WOOF!!! = processor(speaker⟨Dog A⟩) #?callprocessor94    results→ ['WOOF!!!'].append(resultWOOF!!!)95return results
  61. result ← MEOW!!!, results ← ['WOOF!!!', 'MEOW!!!']

    92for speaker in speakers:93    result→ MEOW!!! = processor(speaker⟨Cat B⟩) #?callprocessor94    results→ ['WOOF!!!', 'MEOW!!!'].append(resultMEOW!!!)95return results
  62. result ← BEEP BOOP!!!, results ← ['WOOF!!!', 'MEOW!!!', 'BEEP BOOP!!!']

    92for speaker in speakers:93    result→ BEEP BOOP!!! = processor(speaker⟨Robot C⟩) #?callprocessor94    results→ ['WOOF!!!', 'MEOW!!!', 'BEEP BOOP!!!'].append(resultBEEP BOOP!!!)95return results['WOOF!!!', 'MEOW!!!', 'BEEP BOOP!!!']
  63. results ← ['WOOF!!!', 'MEOW!!!', 'BEEP BOOP!!!']

    142print("Loud processor:")143results→ ['WOOF!!!', 'MEOW!!!', 'BEEP BOOP!!!'] = process_speakers(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩], loud_speak⟨function loud_speak D⟩) #?processloud144for r in results:
  64. for r in results:

    pass 1 of 3
    143results = process_speakers(speakers, loud_speak) #?processloud144for rWOOF!!! in results['WOOF!!!', 'MEOW!!!', 'BEEP BOOP!!!']:145    print(f"  {rWOOF!!!}")
    output  WOOF!!!
    All 3 passes — pass 1 is the card above
    passr
    1WOOF!!!
    2MEOW!!!
    3BEEP BOOP!!!
  65. results = process_speakers(speakers, count_speak) #?processcount

    147print("\nCount processor:")148results = process_speakers(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩], count_speak⟨function count_speak E⟩) #?processcount149for r in results:
    output
    Count processor:
  66. results ← []

    pass 2 of 2
    85# Higher-order polymorphic function #?higherorder86def process_speakers(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩], processor⟨function count_speak E⟩): #?processspeakers87    """88    Apply any processor function to speakers.89    Both the speakers and processor are polymorphic!90    """91    results→ [] = []92    for speaker in speakers:
  67. def count_speak(s): #?countspeak

    pass 1 of 3
    139def count_speak(s⟨Dog A⟩): #?countspeak140    return f"{s⟨Dog A⟩.speak()} ({len(s.speak())} chars)"
    All 3 passes — pass 1 is the card above
    passs
    1⟨Dog A⟩
    2⟨Cat B⟩
    3⟨Robot C⟩
  68. result ← Woof! (5 chars), results ← ['Woof! (5 chars)']

    92for speaker in speakers:93    result→ Woof! (5 chars) = processor(speaker⟨Dog A⟩) #?callprocessor94    results→ ['Woof! (5 chars)'].append(resultWoof! (5 chars))95return results
  69. result ← Meow! (5 chars), results ← ['Woof! (5 chars)', 'Meow! (5 chars)']

    92for speaker in speakers:93    result→ Meow! (5 chars) = processor(speaker⟨Cat B⟩) #?callprocessor94    results→ ['Woof! (5 chars)', 'Meow! (5 chars)'].append(resultMeow! (5 chars))95return results
  70. result ← Beep boop! (10 chars), results ← ['Woof! (5 chars)', 'Meow! (5 chars)', 'Beep boop! (10 chars)']

    92for speaker in speakers:93    result→ Beep boop! (10 chars) = processor(speaker⟨Robot C⟩) #?callprocessor94    results→ ['Woof! (5 chars)', 'Meow! (5 chars)', 'Beep boop! (10 chars)'].append(resultBeep boop! (10 chars))95return results['Woof! (5 chars)', 'Meow! (5 chars)', 'Beep boop! (10 chars)']
  71. results ← ['Woof! (5 chars)', 'Meow! (5 chars)', 'Beep boop! (10 chars)']

    147print("\nCount processor:")148results→ ['Woof! (5 chars)', 'Meow! (5 chars)', 'Beep boop! (10 chars)'] = process_speakers(speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩], count_speak⟨function count_speak E⟩) #?processcount149for r in results:
  72. for r in results:

    pass 1 of 3
    148results = process_speakers(speakers, count_speak) #?processcount149for rWoof! (5 chars) in results['Woof! (5 chars)', 'Meow! (5 chars)', 'Beep boop! (10 chars)']:150    print(f"  {rWoof! (5 chars)}")
    output  Woof! (5 chars)
    All 3 passes — pass 1 is the card above
    passr
    1Woof! (5 chars)
    2Meow! (5 chars)
    3Beep boop! (10 chars)
  73. print(" === Polymorphic Function Benefits ===")

    152print("\n=== Polymorphic Function Benefits ===")153print("""1541. Reusable: One function works with many types1552. Flexible: Easy to add new types1563. Clean: No type-checking spaghetti1574. Pythonic: Embraces duck typing158159Best practices:160- Document expected interface161- Use meaningful parameter names162- Handle missing methods gracefully (hasattr)163- Consider type hints for documentation164""")
    output
    === Polymorphic Function Benefits ===
    
    1. Reusable: One function works with many types
    2. Flexible: Easy to add new types
    3. Clean: No type-checking spaghetti
    4. Pythonic: Embraces duck typing
    
    Best practices:
    - Document expected interface
    - Use meaningful parameter names
    - Handle missing methods gracefully (hasattr)
    - Consider type hints for documentation
  1. print("=== Polymorphic Functions === ")

    98print("=== Polymorphic Functions ===\n")99100# Create different objects101dog = Dog("Buddy")102cat = Cat("Whiskers")
    output=== Polymorphic Functions ===
  2. self.name ← Buddy

    3class Dog:4    def __init__(self⟨Dog A⟩, nameBuddy):5        self.name→ Buddy = nameBuddy
  3. dog ← ⟨Dog A⟩

    100# Create different objects101dog→ ⟨Dog A⟩ = Dog("Buddy")102cat = Cat("Whiskers")103robot = Robot("R2D2")
  4. self.name ← Whiskers

    14class Cat:15    def __init__(self⟨Cat B⟩, nameWhiskers):16        self.name→ Whiskers = nameWhiskers
  5. cat ← ⟨Cat B⟩

    101dog = Dog("Buddy")102cat→ ⟨Cat B⟩ = Cat("Whiskers")103robot = Robot("R2D2")
  6. self.model ← R2D2

    25class Robot:26    def __init__(self⟨Robot C⟩, modelR2D2):27        self.model→ R2D2 = modelR2D2
  7. robot ← ⟨Robot C⟩, speakers ← [⟨Dog A⟩, ⟨Robot C⟩]

    102cat = Cat("Whiskers")103robot→ ⟨Robot C⟩ = Robot("R2D2")104105speakers→ [⟨Dog A⟩, ⟨Robot C⟩] = [dog⟨Dog A⟩, robot⟨Robot C⟩]106107# Basic polymorphic function108print("--- make_speak (basic polymorphism) ---")109for s in speakers:
    output--- make_speak (basic polymorphism) ---
  8. for s in speakers:

    pass 1 of 2
    108print("--- make_speak (basic polymorphism) ---")109for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:110    print(f"  {make_speak(s⟨Dog A⟩)}")
  9. def make_speak(speaker):

    pass 1 of 6
    36# Polymorphic function - works with any object that has speak()37def make_speak(speaker⟨Dog A⟩):38    """39    Works with any object that has a speak() method.40    This is polymorphism via duck typing.41    """42    return speaker⟨Dog A⟩.speak()
    All 6 passes — pass 1 is the card above
    passspeaker
    1⟨Dog A⟩
    2⟨Robot C⟩
    3⟨Dog A⟩
    4⟨Robot C⟩
    5⟨Dog A⟩
    6⟨Robot C⟩
  10. def speak(self):

    pass 1 of 7
    7def speak(self⟨Dog A⟩):8    return "Woof!"
  11. print(f" {make_speak(s)}")

    109for s in speakers:110    print(f"  {make_speak(s⟨Dog A⟩)}")
    output  Woof!
  12. for s in speakers:

    pass 2 of 2
    108print("--- make_speak (basic polymorphism) ---")109for s⟨Robot C⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:110    print(f"  {make_speak(s⟨Robot C⟩)}")
  13. def speak(self):

    pass 1 of 7
    29def speak(self⟨Robot C⟩):30    return "Beep boop!"
  14. print(f" {make_speak(s)}")

    109for s in speakers:110    print(f"  {make_speak(s⟨Robot C⟩)}")
    output  Beep boop!
  15. print(" --- describe_speaker (with type handling) ---")

    112# Polymorphic with type handling113print("\n--- describe_speaker (with type handling) ---")114for s in speakers:
    output
    --- describe_speaker (with type handling) ---
  16. for s in speakers:

    pass 1 of 2
    113print("\n--- describe_speaker (with type handling) ---")114for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:115    print(f"  {describe_speaker(s⟨Dog A⟩)}")
  17. def describe_speaker(speaker):

    pass 1 of 2
    45# Function with type-specific logic46def describe_speaker(speaker⟨Dog A⟩):47    """48    Polymorphic function that also handles type differences.49    """50    sound = speaker⟨Dog A⟩.speak()
  18. sound ← Woof!

    49"""50sound→ Woof! = speaker⟨Dog A⟩.speak()
  19. identifier ← Buddy

    52# Can use hasattr to check for attributes53if hasattr(speaker⟨Dog A⟩, 'name'):54    identifier→ Buddy = speaker.nameBuddy55elif hasattr(speaker, 'model'):
  20. return f"{identifier} says: {sound}"

    60return f"{identifierBuddy} says: {soundWoof!}"
  21. print(f" {describe_speaker(s)}")

    114for s in speakers:115    print(f"  {describe_speaker(s⟨Dog A⟩)}")
    output  Buddy says: Woof!
  22. for s in speakers:

    pass 2 of 2
    113print("\n--- describe_speaker (with type handling) ---")114for s⟨Robot C⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:115    print(f"  {describe_speaker(s⟨Robot C⟩)}")
  23. def describe_speaker(speaker):

    pass 2 of 2
    45# Function with type-specific logic46def describe_speaker(speaker⟨Robot C⟩):47    """48    Polymorphic function that also handles type differences.49    """50    sound = speaker⟨Robot C⟩.speak()
  24. sound ← Beep boop!

    49"""50sound→ Beep boop! = speaker⟨Robot C⟩.speak()
  25. identifier ← R2D2

    54    identifier = speaker.name55elif hasattr(speaker⟨Robot C⟩, 'model'):56    identifier→ R2D2 = speaker.modelR2D257else:
  26. return f"{identifier} says: {sound}"

    60return f"{identifierR2D2} says: {soundBeep boop!}"
  27. print(f" {describe_speaker(s)}")

    114for s in speakers:115    print(f"  {describe_speaker(s⟨Robot C⟩)}")
    output  R2D2 says: Beep boop!
  28. print(" --- show_length (built-in len polymorphism) ---")

    117# Built-in len() polymorphism118print("\n--- show_length (built-in len polymorphism) ---")119for s in speakers:
    output
    --- show_length (built-in len polymorphism) ---
  29. for s in speakers:

    pass 1 of 2
    118print("\n--- show_length (built-in len polymorphism) ---")119for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:120    print(f"  {show_length(s⟨Dog A⟩)}")
  30. def show_length(obj):

    pass 1 of 5
    63# Function using len() - built-in polymorphism64def show_length(obj⟨Dog A⟩):65    """66    len() is polymorphic - works with many types.67    Requires __len__ method.68    """69    return f"Length of {type(obj⟨Dog A⟩).__name__}: {len(obj)}"
    All 5 passes — pass 1 is the card above
    passobjselfself.nameself.model
    1⟨Dog A⟩⟨Dog A⟩Buddy
    2⟨Robot C⟩⟨Robot C⟩R2D2
    3Hello
    4[1, 2, 3]
    5{'a': 1, 'b': 2}
  31. def __len__(self):

    10def __len__(self⟨Dog A⟩):11    return len(self.nameBuddy)
  32. print(f" {show_length(s)}")

    119for s in speakers:120    print(f"  {show_length(s⟨Dog A⟩)}")
    output  Length of Dog: 5
  33. for s in speakers:

    pass 2 of 2
    118print("\n--- show_length (built-in len polymorphism) ---")119for s⟨Robot C⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:120    print(f"  {show_length(s⟨Robot C⟩)}")
  34. def __len__(self):

    32def __len__(self⟨Robot C⟩):33    return len(self.modelR2D2)
  35. print(f" {show_length(s)}")

    119for s in speakers:120    print(f"  {show_length(s⟨Robot C⟩)}")
    output  Length of Robot: 4
  36. print(" Also works with built-in types:")

    122# Also works with built-in types!123print("\n  Also works with built-in types:")124print(f"  {show_length('Hello')}")125print(f"  {show_length([1, 2, 3])}")
    output
      Also works with built-in types:
  37. print(f" {show_length('Hello')}")

    123print("\n  Also works with built-in types:")124print(f"  {show_length('Hello')}")125print(f"  {show_length([1, 2, 3])}")126print(f"  {show_length({'a': 1, 'b': 2})}")
    output  Length of str: 5
  38. print(f" {show_length([1, 2, 3])}")

    124print(f"  {show_length('Hello')}")125print(f"  {show_length([1, 2, 3])}")126print(f"  {show_length({'a': 1, 'b': 2})}")
    output  Length of list: 3
  39. make_chorus(speakers)

    125print(f"  {show_length([1, 2, 3])}")126print(f"  {show_length({'a': 1, 'b': 2})}")127128# Chorus function129make_chorus(speakers[⟨Dog A⟩, ⟨Robot C⟩])
    output  Length of dict: 2
  40. def make_chorus(speakers, times=2):

    72# Function accepting multiple types with shared behavior73def make_chorus(speakers[⟨Dog A⟩, ⟨Robot C⟩], times2=2):74    """75    Make all speakers speak multiple times.76    Works with any list of objects with speak() method.77    """78    print("\n** Chorus Time! **")79    for _ in range(times):
    output
    ** Chorus Time! **
  41. for _ in range(times):

    pass 1 of 2
    78print("\n** Chorus Time! **")79for _0 in range(times2):80    for speaker in speakers:81        print(f"  {make_speak(speaker)}")
  42. for speaker in speakers:

    pass 1 of 4
    79for _ in range(times):80    for speaker⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:81        print(f"  {make_speak(speaker⟨Dog A⟩)}")82    print("---")
    All 4 passes — pass 1 is the card above
    passspeaker
    1⟨Dog A⟩
    2⟨Robot C⟩
    3⟨Dog A⟩
    4⟨Robot C⟩
  43. print(f" {make_speak(speaker)}")

    80for speaker in speakers:81    print(f"  {make_speak(speaker⟨Dog A⟩)}")82print("---")
    output  Woof!
  44. print(f" {make_speak(speaker)}")

    80for speaker in speakers:81    print(f"  {make_speak(speaker⟨Robot C⟩)}")82print("---")
    output  Beep boop!
  45. print("---")

    81    print(f"  {make_speak(speaker)}")82print("---")
    output---
  46. for _ in range(times):

    pass 2 of 2
    78print("\n** Chorus Time! **")79for _1 in range(times2):80    for speaker in speakers:81        print(f"  {make_speak(speaker)}")
  47. print(f" {make_speak(speaker)}")

    80for speaker in speakers:81    print(f"  {make_speak(speaker⟨Dog A⟩)}")82print("---")
    output  Woof!
  48. print(f" {make_speak(speaker)}")

    80for speaker in speakers:81    print(f"  {make_speak(speaker⟨Robot C⟩)}")82print("---")
    output  Beep boop!
  49. print("---")

    81    print(f"  {make_speak(speaker)}")82print("---")
    output---
  50. make_chorus(speakers)

    128# Chorus function129make_chorus(speakers[⟨Dog A⟩, ⟨Robot C⟩])130131# Higher-order function132print("\n--- process_speakers (higher-order) ---")133134# Different processors135def loud_speak(s):136    return s.speak().upper() + "!!"137138def count_speak(s):139    return f"{s.speak()} ({len(s.speak())} chars)"140141print("Loud processor:")142results = process_speakers(speakers[⟨Dog A⟩, ⟨Robot C⟩], loud_speak⟨function loud_speak D⟩)143for r in results:
    output
    --- process_speakers (higher-order) ---
    Loud processor:
  51. results ← []

    pass 1 of 2
    85# Higher-order polymorphic function86def process_speakers(speakers[⟨Dog A⟩, ⟨Robot C⟩], processor⟨function loud_speak D⟩):87    """88    Apply any processor function to speakers.89    Both the speakers and processor are polymorphic!90    """91    results→ [] = []92    for speaker in speakers:
  52. for speaker in speakers:

    pass 1 of 4
    91results = []92for speaker⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:93    result = processor(speaker⟨Dog A⟩)94    results.append(result)
    All 4 passes — pass 1 is the card above
    passspeakers
    1⟨Dog A⟩⟨Dog A⟩
    2⟨Robot C⟩⟨Robot C⟩
    3⟨Dog A⟩⟨Dog A⟩
    4⟨Robot C⟩⟨Robot C⟩
  53. def loud_speak(s):

    pass 1 of 2
    134# Different processors135def loud_speak(s⟨Dog A⟩):136    return s⟨Dog A⟩.speak().upper() + "!!"
  54. result ← WOOF!!!, results ← ['WOOF!!!']

    92for speaker in speakers:93    result→ WOOF!!! = processor(speaker⟨Dog A⟩)94    results→ ['WOOF!!!'].append(resultWOOF!!!)95return results
  55. def loud_speak(s):

    pass 2 of 2
    134# Different processors135def loud_speak(s⟨Robot C⟩):136    return s⟨Robot C⟩.speak().upper() + "!!"
  56. result ← BEEP BOOP!!!, results ← ['WOOF!!!', 'BEEP BOOP!!!']

    92for speaker in speakers:93    result→ BEEP BOOP!!! = processor(speaker⟨Robot C⟩)94    results→ ['WOOF!!!', 'BEEP BOOP!!!'].append(resultBEEP BOOP!!!)95return results['WOOF!!!', 'BEEP BOOP!!!']
  57. results ← ['WOOF!!!', 'BEEP BOOP!!!']

    141print("Loud processor:")142results→ ['WOOF!!!', 'BEEP BOOP!!!'] = process_speakers(speakers[⟨Dog A⟩, ⟨Robot C⟩], loud_speak⟨function loud_speak D⟩)143for r in results:
  58. for r in results:

    pass 1 of 2
    142results = process_speakers(speakers, loud_speak)143for rWOOF!!! in results['WOOF!!!', 'BEEP BOOP!!!']:144    print(f"  {rWOOF!!!}")
    output  WOOF!!!
  59. for r in results:

    pass 2 of 2
    142results = process_speakers(speakers, loud_speak)143for rBEEP BOOP!!! in results['WOOF!!!', 'BEEP BOOP!!!']:144    print(f"  {rBEEP BOOP!!!}")
    output  BEEP BOOP!!!
  60. results = process_speakers(speakers, count_speak)

    146print("\nCount processor:")147results = process_speakers(speakers[⟨Dog A⟩, ⟨Robot C⟩], count_speak⟨function count_speak E⟩)148for r in results:
    output
    Count processor:
  61. results ← []

    pass 2 of 2
    85# Higher-order polymorphic function86def process_speakers(speakers[⟨Dog A⟩, ⟨Robot C⟩], processor⟨function count_speak E⟩):87    """88    Apply any processor function to speakers.89    Both the speakers and processor are polymorphic!90    """91    results→ [] = []92    for speaker in speakers:
  62. def count_speak(s):

    pass 1 of 2
    138def count_speak(s⟨Dog A⟩):139    return f"{s⟨Dog A⟩.speak()} ({len(s.speak())} chars)"
  63. result ← Woof! (5 chars), results ← ['Woof! (5 chars)']

    92for speaker in speakers:93    result→ Woof! (5 chars) = processor(speaker⟨Dog A⟩)94    results→ ['Woof! (5 chars)'].append(resultWoof! (5 chars))95return results
  64. def count_speak(s):

    pass 2 of 2
    138def count_speak(s⟨Robot C⟩):139    return f"{s⟨Robot C⟩.speak()} ({len(s.speak())} chars)"
  65. result ← Beep boop! (10 chars), results ← ['Woof! (5 chars)', 'Beep boop! (10 chars)']

    92for speaker in speakers:93    result→ Beep boop! (10 chars) = processor(speaker⟨Robot C⟩)94    results→ ['Woof! (5 chars)', 'Beep boop! (10 chars)'].append(resultBeep boop! (10 chars))95return results['Woof! (5 chars)', 'Beep boop! (10 chars)']
  66. results ← ['Woof! (5 chars)', 'Beep boop! (10 chars)']

    146print("\nCount processor:")147results→ ['Woof! (5 chars)', 'Beep boop! (10 chars)'] = process_speakers(speakers[⟨Dog A⟩, ⟨Robot C⟩], count_speak⟨function count_speak E⟩)148for r in results:
  67. for r in results:

    pass 1 of 2
    147results = process_speakers(speakers, count_speak)148for rWoof! (5 chars) in results['Woof! (5 chars)', 'Beep boop! (10 chars)']:149    print(f"  {rWoof! (5 chars)}")
    output  Woof! (5 chars)
  68. for r in results:

    pass 2 of 2
    147results = process_speakers(speakers, count_speak)148for rBeep boop! (10 chars) in results['Woof! (5 chars)', 'Beep boop! (10 chars)']:149    print(f"  {rBeep boop! (10 chars)}")
    output  Beep boop! (10 chars)
  69. print(" === Polymorphic Function Benefits ===")

    151print("\n=== Polymorphic Function Benefits ===")152print("""1531. Reusable: One function works with many types1542. Flexible: Easy to add new types1553. Clean: No type-checking spaghetti1564. Pythonic: Embraces duck typing157158Best practices:159- Document expected interface160- Use meaningful parameter names161- Handle missing methods gracefully (hasattr)162- Consider type hints for documentation163""")
    output
    === Polymorphic Function Benefits ===
    
    1. Reusable: One function works with many types
    2. Flexible: Easy to add new types
    3. Clean: No type-checking spaghetti
    4. Pythonic: Embraces duck typing
    
    Best practices:
    - Document expected interface
    - Use meaningful parameter names
    - Handle missing methods gracefully (hasattr)
    - Consider type hints for documentation
  1. print("=== Polymorphic Functions === ")

    98print("=== Polymorphic Functions ===\n")99100# Create different objects101dog = Dog("Buddy")102cat = Cat("Whiskers")
    output=== Polymorphic Functions ===
  2. self.name ← Buddy

    3class Dog:4    def __init__(self⟨Dog A⟩, nameBuddy):5        self.name→ Buddy = nameBuddy
  3. dog ← ⟨Dog A⟩

    100# Create different objects101dog→ ⟨Dog A⟩ = Dog("Buddy")102cat = Cat("Whiskers")103robot = Robot("R2D2")
  4. self.name ← Whiskers

    14class Cat:15    def __init__(self⟨Cat B⟩, nameWhiskers):16        self.name→ Whiskers = nameWhiskers
  5. cat ← ⟨Cat B⟩

    101dog = Dog("Buddy")102cat→ ⟨Cat B⟩ = Cat("Whiskers")103robot = Robot("R2D2")
  6. self.model ← R2D2

    25class Robot:26    def __init__(self⟨Robot C⟩, modelR2D2):27        self.model→ R2D2 = modelR2D2
  7. robot ← ⟨Robot C⟩, speakers ← [⟨Cat B⟩]

    102cat = Cat("Whiskers")103robot→ ⟨Robot C⟩ = Robot("R2D2")104105speakers→ [⟨Cat B⟩] = [cat⟨Cat B⟩]106107# Basic polymorphic function108print("--- make_speak (basic polymorphism) ---")109for s in speakers:
    output--- make_speak (basic polymorphism) ---
  8. for s in speakers:

    108print("--- make_speak (basic polymorphism) ---")109for s⟨Cat B⟩ in speakers[⟨Cat B⟩]:110    print(f"  {make_speak(s⟨Cat B⟩)}")
  9. def make_speak(speaker):

    pass 1 of 3
    36# Polymorphic function - works with any object that has speak()37def make_speak(speaker⟨Cat B⟩):38    """39    Works with any object that has a speak() method.40    This is polymorphism via duck typing.41    """42    return speaker⟨Cat B⟩.speak()
  10. def speak(self):

    pass 1 of 7
    18def speak(self⟨Cat B⟩):19    return "Meow!"
  11. print(f" {make_speak(s)}")

    109for s in speakers:110    print(f"  {make_speak(s⟨Cat B⟩)}")
    output  Meow!
  12. print(" --- describe_speaker (with type handling) ---")

    112# Polymorphic with type handling113print("\n--- describe_speaker (with type handling) ---")114for s in speakers:
    output
    --- describe_speaker (with type handling) ---
  13. for s in speakers:

    113print("\n--- describe_speaker (with type handling) ---")114for s⟨Cat B⟩ in speakers[⟨Cat B⟩]:115    print(f"  {describe_speaker(s⟨Cat B⟩)}")
  14. def describe_speaker(speaker):

    45# Function with type-specific logic46def describe_speaker(speaker⟨Cat B⟩):47    """48    Polymorphic function that also handles type differences.49    """50    sound = speaker⟨Cat B⟩.speak()
  15. sound ← Meow!

    49"""50sound→ Meow! = speaker⟨Cat B⟩.speak()
  16. identifier ← Whiskers

    52# Can use hasattr to check for attributes53if hasattr(speaker⟨Cat B⟩, 'name'):54    identifier→ Whiskers = speaker.nameWhiskers55elif hasattr(speaker, 'model'):
  17. return f"{identifier} says: {sound}"

    60return f"{identifierWhiskers} says: {soundMeow!}"
  18. print(f" {describe_speaker(s)}")

    114for s in speakers:115    print(f"  {describe_speaker(s⟨Cat B⟩)}")
    output  Whiskers says: Meow!
  19. print(" --- show_length (built-in len polymorphism) ---")

    117# Built-in len() polymorphism118print("\n--- show_length (built-in len polymorphism) ---")119for s in speakers:
    output
    --- show_length (built-in len polymorphism) ---
  20. for s in speakers:

    118print("\n--- show_length (built-in len polymorphism) ---")119for s⟨Cat B⟩ in speakers[⟨Cat B⟩]:120    print(f"  {show_length(s⟨Cat B⟩)}")
  21. def show_length(obj):

    pass 1 of 4
    63# Function using len() - built-in polymorphism64def show_length(obj⟨Cat B⟩):65    """66    len() is polymorphic - works with many types.67    Requires __len__ method.68    """69    return f"Length of {type(obj⟨Cat B⟩).__name__}: {len(obj)}"
    All 4 passes — pass 1 is the card above
    passobjselfself.name
    1⟨Cat B⟩⟨Cat B⟩Whiskers
    2Hello
    3[1, 2, 3]
    4{'a': 1, 'b': 2}
  22. def __len__(self):

    21def __len__(self⟨Cat B⟩):22    return len(self.nameWhiskers)
  23. print(f" {show_length(s)}")

    119for s in speakers:120    print(f"  {show_length(s⟨Cat B⟩)}")
    output  Length of Cat: 8
  24. print(" Also works with built-in types:")

    122# Also works with built-in types!123print("\n  Also works with built-in types:")124print(f"  {show_length('Hello')}")125print(f"  {show_length([1, 2, 3])}")
    output
      Also works with built-in types:
  25. print(f" {show_length('Hello')}")

    123print("\n  Also works with built-in types:")124print(f"  {show_length('Hello')}")125print(f"  {show_length([1, 2, 3])}")126print(f"  {show_length({'a': 1, 'b': 2})}")
    output  Length of str: 5
  26. print(f" {show_length([1, 2, 3])}")

    124print(f"  {show_length('Hello')}")125print(f"  {show_length([1, 2, 3])}")126print(f"  {show_length({'a': 1, 'b': 2})}")
    output  Length of list: 3
  27. make_chorus(speakers)

    125print(f"  {show_length([1, 2, 3])}")126print(f"  {show_length({'a': 1, 'b': 2})}")127128# Chorus function129make_chorus(speakers[⟨Cat B⟩])
    output  Length of dict: 2
  28. def make_chorus(speakers, times=2):

    72# Function accepting multiple types with shared behavior73def make_chorus(speakers[⟨Cat B⟩], times2=2):74    """75    Make all speakers speak multiple times.76    Works with any list of objects with speak() method.77    """78    print("\n** Chorus Time! **")79    for _ in range(times):
    output
    ** Chorus Time! **
  29. for _ in range(times):

    pass 1 of 2
    78print("\n** Chorus Time! **")79for _0 in range(times2):80    for speaker in speakers:81        print(f"  {make_speak(speaker)}")
  30. for speaker in speakers:

    pass 1 of 2
    79for _ in range(times):80    for speaker⟨Cat B⟩ in speakers[⟨Cat B⟩]:81        print(f"  {make_speak(speaker⟨Cat B⟩)}")82    print("---")
  31. print(f" {make_speak(speaker)}")

    80for speaker in speakers:81    print(f"  {make_speak(speaker⟨Cat B⟩)}")82print("---")
    output  Meow!
  32. print("---")

    81    print(f"  {make_speak(speaker)}")82print("---")
    output---
  33. for _ in range(times):

    pass 2 of 2
    78print("\n** Chorus Time! **")79for _1 in range(times2):80    for speaker in speakers:81        print(f"  {make_speak(speaker)}")
  34. for speaker in speakers:

    pass 2 of 2
    79for _ in range(times):80    for speaker⟨Cat B⟩ in speakers[⟨Cat B⟩]:81        print(f"  {make_speak(speaker⟨Cat B⟩)}")82    print("---")
  35. print(f" {make_speak(speaker)}")

    80for speaker in speakers:81    print(f"  {make_speak(speaker⟨Cat B⟩)}")82print("---")
    output  Meow!
  36. print("---")

    81    print(f"  {make_speak(speaker)}")82print("---")
    output---
  37. make_chorus(speakers)

    128# Chorus function129make_chorus(speakers[⟨Cat B⟩])130131# Higher-order function132print("\n--- process_speakers (higher-order) ---")133134# Different processors135def loud_speak(s):136    return s.speak().upper() + "!!"137138def count_speak(s):139    return f"{s.speak()} ({len(s.speak())} chars)"140141print("Loud processor:")142results = process_speakers(speakers[⟨Cat B⟩], loud_speak⟨function loud_speak D⟩)143for r in results:
    output
    --- process_speakers (higher-order) ---
    Loud processor:
  38. results ← []

    pass 1 of 2
    85# Higher-order polymorphic function86def process_speakers(speakers[⟨Cat B⟩], processor⟨function loud_speak D⟩):87    """88    Apply any processor function to speakers.89    Both the speakers and processor are polymorphic!90    """91    results→ [] = []92    for speaker in speakers:
  39. for speaker in speakers:

    pass 1 of 2
    91results = []92for speaker⟨Cat B⟩ in speakers[⟨Cat B⟩]:93    result = processor(speaker⟨Cat B⟩)94    results.append(result)
  40. def loud_speak(s):

    134# Different processors135def loud_speak(s⟨Cat B⟩):136    return s⟨Cat B⟩.speak().upper() + "!!"
  41. result ← MEOW!!!, results ← ['MEOW!!!']

    92for speaker in speakers:93    result→ MEOW!!! = processor(speaker⟨Cat B⟩)94    results→ ['MEOW!!!'].append(resultMEOW!!!)95return results['MEOW!!!']
  42. results ← ['MEOW!!!']

    141print("Loud processor:")142results→ ['MEOW!!!'] = process_speakers(speakers[⟨Cat B⟩], loud_speak⟨function loud_speak D⟩)143for r in results:
  43. for r in results:

    142results = process_speakers(speakers, loud_speak)143for rMEOW!!! in results['MEOW!!!']:144    print(f"  {rMEOW!!!}")
    output  MEOW!!!
  44. results = process_speakers(speakers, count_speak)

    146print("\nCount processor:")147results = process_speakers(speakers[⟨Cat B⟩], count_speak⟨function count_speak E⟩)148for r in results:
    output
    Count processor:
  45. results ← []

    pass 2 of 2
    85# Higher-order polymorphic function86def process_speakers(speakers[⟨Cat B⟩], processor⟨function count_speak E⟩):87    """88    Apply any processor function to speakers.89    Both the speakers and processor are polymorphic!90    """91    results→ [] = []92    for speaker in speakers:
  46. for speaker in speakers:

    pass 2 of 2
    91results = []92for speaker⟨Cat B⟩ in speakers[⟨Cat B⟩]:93    result = processor(speaker⟨Cat B⟩)94    results.append(result)
  47. def count_speak(s):

    138def count_speak(s⟨Cat B⟩):139    return f"{s⟨Cat B⟩.speak()} ({len(s.speak())} chars)"
  48. result ← Meow! (5 chars), results ← ['Meow! (5 chars)']

    92for speaker in speakers:93    result→ Meow! (5 chars) = processor(speaker⟨Cat B⟩)94    results→ ['Meow! (5 chars)'].append(resultMeow! (5 chars))95return results['Meow! (5 chars)']
  49. results ← ['Meow! (5 chars)']

    146print("\nCount processor:")147results→ ['Meow! (5 chars)'] = process_speakers(speakers[⟨Cat B⟩], count_speak⟨function count_speak E⟩)148for r in results:
  50. for r in results:

    147results = process_speakers(speakers, count_speak)148for rMeow! (5 chars) in results['Meow! (5 chars)']:149    print(f"  {rMeow! (5 chars)}")
    output  Meow! (5 chars)
  51. print(" === Polymorphic Function Benefits ===")

    151print("\n=== Polymorphic Function Benefits ===")152print("""1531. Reusable: One function works with many types1542. Flexible: Easy to add new types1553. Clean: No type-checking spaghetti1564. Pythonic: Embraces duck typing157158Best practices:159- Document expected interface160- Use meaningful parameter names161- Handle missing methods gracefully (hasattr)162- Consider type hints for documentation163""")
    output
    === Polymorphic Function Benefits ===
    
    1. Reusable: One function works with many types
    2. Flexible: Easy to add new types
    3. Clean: No type-checking spaghetti
    4. Pythonic: Embraces duck typing
    
    Best practices:
    - Document expected interface
    - Use meaningful parameter names
    - Handle missing methods gracefully (hasattr)
    - Consider type hints for documentation

Function accepts anything with required methods. Very flexible.

Operator overloading

Polymorphism for operators like +, -, ==.

operator_overload.py
Replay: real traced execution (multi-file project)
# Polymorphism via Operator Overloading

class Vector:
    """2D Vector with operator overloading."""

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

    def __add__(self, other):
        """v1 + v2"""
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        """v1 - v2"""
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar):
        """v * scalar"""
        return Vector(self.x * scalar, self.y * scalar)

    def __rmul__(self, scalar):
        """scalar * v (reversed)"""
        return self.__mul__(scalar)

    def __eq__(self, other):
        """v1 == v2"""
        return self.x == other.x and self.y == other.y

    def __abs__(self):
        """abs(v) - magnitude"""
        return (self.x ** 2 + self.y ** 2) ** 0.5


class Money:
    """Money class with operator overloading."""

    def __init__(self, dollars, cents=0):
        total_cents = dollars * 100 + cents
        self.dollars = total_cents // 100
        self.cents = total_cents % 100

    def __repr__(self):
        return f"Money({self.dollars}, {self.cents})"

    def __str__(self):
        return f"${self.dollars}.{self.cents:02d}"

    def __add__(self, other):
        """m1 + m2"""
        total_cents = (self.dollars * 100 + self.cents +
                      other.dollars * 100 + other.cents)
        return Money(total_cents // 100, total_cents % 100)

    def __sub__(self, other):
        """m1 - m2"""
        total_cents = (self.dollars * 100 + self.cents -
                      other.dollars * 100 - other.cents)
        return Money(total_cents // 100, total_cents % 100)

    def __mul__(self, factor):
        """m * factor"""
        total_cents = int((self.dollars * 100 + self.cents) * factor)
        return Money(total_cents // 100, total_cents % 100)

    def __rmul__(self, factor):
        return self.__mul__(factor)

    def __lt__(self, other):
        """m1 < m2"""
        return (self.dollars * 100 + self.cents) < (other.dollars * 100 + other.cents)

    def __le__(self, other):
        """m1 <= m2"""
        return (self.dollars * 100 + self.cents) <= (other.dollars * 100 + other.cents)

    def __eq__(self, other):
        """m1 == m2"""
        return self.dollars == other.dollars and self.cents == other.cents


class StringList:
    """List-like class with operator overloading."""

    def __init__(self, *items):
        self.items = list(items)

    def __repr__(self):
        return f"StringList{tuple(self.items)}"

    def __add__(self, other):
        """Concatenate two StringLists"""
        return StringList(*(self.items + other.items))

    def __len__(self):
        """len(sl)"""
        return len(self.items)

    def __getitem__(self, index):
        """sl[index]"""
        return self.items[index]

    def __contains__(self, item):
        """item in sl"""
        return item in self.items

    def __iter__(self):
        """for item in sl"""
        return iter(self.items)


print("=== Operator Overloading ===\n")

# Vector operations
print("--- Vector Operations ---")
v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(f"v1 = {v1}")
print(f"v2 = {v2}")

print(f"v1 + v2 = {v1 + v2}")
print(f"v1 - v2 = {v1 - v2}")
print(f"v1 * 2 = {v1 * 2}")
print(f"3 * v2 = {3 * v2}")
print(f"|v1| = {abs(v1):.2f}")
print(f"v1 == v2: {v1 == v2}")
print(f"v1 == Vector(3, 4): {v1 == Vector(3, 4)}")

# Money operations
print("\n--- Money Operations ---")
price = Money(19, 99)
tax = Money(1, 60)
discount = Money(5, 0)

print(f"Price: {price}")
print(f"Tax: {tax}")
print(f"Discount: {discount}")

total = price + tax
print(f"Price + Tax = {total}")

final = total - discount
print(f"After discount = {final}")

doubled = price * 2
print(f"Price * 2 = {doubled}")

print(f"Tax < Discount: {tax < discount}")
print(f"Price == Money(19, 99): {price == Money(19, 99)}")

# StringList operations
print("\n--- StringList Operations ---")
fruits = StringList("apple", "banana")
veggies = StringList("carrot", "broccoli")

print(f"fruits = {fruits}")
print(f"veggies = {veggies}")

combined = fruits + veggies
print(f"combined = {combined}")

print(f"len(combined) = {len(combined)}")
print(f"combined[0] = {combined[0]}")
print(f"'apple' in combined: {'apple' in combined}")

print("Iterating:")
for item in combined:
    print(f"  - {item}")

print("\n=== Common Operators ===")
print("""
Arithmetic:
  __add__(self, other)    +
  __sub__(self, other)    -
  __mul__(self, other)    *
  __truediv__(self, other) /
  __rmul__(self, other)   reversed * (scalar * obj)

Comparison:
  __eq__(self, other)     ==
  __ne__(self, other)     !=
  __lt__(self, other)     <
  __le__(self, other)     <=
  __gt__(self, other)     >
  __ge__(self, other)     >=

Container:
  __len__(self)           len(obj)
  __getitem__(self, key)  obj[key]
  __contains__(self, item) item in obj
  __iter__(self)          for x in obj

Other:
  __repr__(self)          repr(obj), print in shell
  __str__(self)           str(obj), print()
  __abs__(self)           abs(obj)
  __bool__(self)          bool(obj), if obj
""")

  1. """2D Vector with operator overloading."""

    3class Vector: #?vectorclass4    """2D Vector with operator overloading."""5    6    def __init__(self, x, y): #?vectorinit7        self.x = x8        self.y = y9    10    def __repr__(self): #?vectorrepr11        return f"Vector({self.x}, {self.y})"12    13    def __add__(self, other): #?vectoradd14        """v1 + v2"""15        return Vector(self.x + other.x, self.y + other.y) #?addreturn16    17    def __sub__(self, other): #?vectorsub18        """v1 - v2"""19        return Vector(self.x - other.x, self.y - other.y)20    21    def __mul__(self, scalar): #?vectormul22        """v * scalar"""23        return Vector(self.x * scalar, self.y * scalar) #?mulreturn24    25    def __rmul__(self, scalar): #?vectorrmul26        """scalar * v (reversed)"""27        return self.__mul__(scalar) #?rmuldelegate28    29    def __eq__(self, other): #?vectoreq30        """v1 == v2"""31        return self.x == other.x and self.y == other.y #?eqreturn32    33    def __abs__(self): #?vectorabs34        """abs(v) - magnitude"""35        return (self.x ** 2 + self.y ** 2) ** 0.5 #?magnitude363738class Money: #?moneyclass39    """Money class with operator overloading."""40    41    def __init__(self, dollars, cents=0): #?moneyinit42        total_cents = dollars * 100 + cents #?totalcents43        self.dollars = total_cents // 100 #?normalize44        self.cents = total_cents % 10045    46    def __repr__(self):47        return f"Money({self.dollars}, {self.cents})"48    49    def __str__(self): #?moneystr50        return f"${self.dollars}.{self.cents:02d}" #?formatcents51    52    def __add__(self, other): #?moneyadd53        """m1 + m2"""54        total_cents = (self.dollars * 100 + self.cents + 55                      other.dollars * 100 + other.cents)56        return Money(total_cents // 100, total_cents % 100)57    58    def __sub__(self, other): #?moneysub59        """m1 - m2"""60        total_cents = (self.dollars * 100 + self.cents - 61                      other.dollars * 100 - other.cents)62        return Money(total_cents // 100, total_cents % 100)63    64    def __mul__(self, factor): #?moneymul65        """m * factor"""66        total_cents = int((self.dollars * 100 + self.cents) * factor)67        return Money(total_cents // 100, total_cents % 100)68    69    def __rmul__(self, factor):70        return self.__mul__(factor)71    72    def __lt__(self, other): #?moneylt73        """m1 < m2"""74        return (self.dollars * 100 + self.cents) < (other.dollars * 100 + other.cents)75    76    def __le__(self, other): #?moneyle77        """m1 <= m2"""78        return (self.dollars * 100 + self.cents) <= (other.dollars * 100 + other.cents)79    80    def __eq__(self, other): #?moneyeq81        """m1 == m2"""82        return self.dollars == other.dollars and self.cents == other.cents838485class StringList: #?stringlistclass86    """List-like class with operator overloading."""87    88    def __init__(self, *items): #?stringlistinit89        self.items = list(items)90    91    def __repr__(self):92        return f"StringList{tuple(self.items)}"93    94    def __add__(self, other): #?stringlistadd95        """Concatenate two StringLists"""96        return StringList(*(self.items + other.items)) #?concatreturn97    98    def __len__(self): #?stringlistlen99        """len(sl)"""100        return len(self.items)101    102    def __getitem__(self, index): #?stringlistget103        """sl[index]"""104        return self.items[index] #?getreturn105    106    def __contains__(self, item): #?stringlistcontains107        """item in sl"""108        return item in self.items #?containsreturn109    110    def __iter__(self): #?stringlistiter111        """for item in sl"""112        return iter(self.items) #?iterreturn113114115print("=== Operator Overloading ===\n")116117# Vector operations #?vectordemo118print("--- Vector Operations ---")119v1 = Vector(3, 4) #?createv1120v2 = Vector(1, 2) #?createv2
    output=== Operator Overloading ===
    --- Vector Operations ---
  2. self.x ← 3, self.y ← 4

    pass 1 of 7
    6def __init__(self(empty), x3, y4): #?vectorinit7    self.x→ 3 = x38    self.y→ 4 = y4
    All 7 passes — pass 1 is the card above
    passxyotherother.xother.yself.xself.y
    13434
    21212
    34646
    42222
    56868
    63636
    734Vector(3, 4)3434
  3. v1 ← Vector(3, 4)

    118print("--- Vector Operations ---")119v1→ Vector(3, 4) = Vector(3, 4) #?createv1120v2 = Vector(1, 2) #?createv2
  4. v2 ← Vector(1, 2)

    119v1 = Vector(3, 4) #?createv1120v2→ Vector(1, 2) = Vector(1, 2) #?createv2121122print(f"v1 = {v1Vector(3, 4)}")123print(f"v2 = {v2Vector(1, 2)}")124125print(f"v1 + v2 = {v1Vector(3, 4) + v2Vector(1, 2)}") #?addvectors126print(f"v1 - v2 = {v1 - v2}") #?subvectors
    outputv1 = Vector(3, 4)
    v2 = Vector(1, 2)
  5. def __add__(self, other): #?vectoradd

    13def __add__(selfVector(3, 4), otherVector(1, 2)): #?vectoradd14    """v1 + v2"""15    return Vector(self.x3 + other.x1, self.y4 + other.y2) #?addreturn
  6. print(f"v1 + v2 = {v1 + v2}") #?addvectors

    125print(f"v1 + v2 = {v1Vector(3, 4) + v2Vector(1, 2)}") #?addvectors126print(f"v1 - v2 = {v1Vector(3, 4) - v2Vector(1, 2)}") #?subvectors127print(f"v1 * 2 = {v1 * 2}") #?mulscalar
    outputv1 + v2 = Vector(4, 6)
  7. def __sub__(self, other): #?vectorsub

    17def __sub__(selfVector(3, 4), otherVector(1, 2)): #?vectorsub18    """v1 - v2"""19    return Vector(self.x3 - other.x1, self.y4 - other.y2)
  8. print(f"v1 - v2 = {v1 - v2}") #?subvectors

    125print(f"v1 + v2 = {v1 + v2}") #?addvectors126print(f"v1 - v2 = {v1Vector(3, 4) - v2Vector(1, 2)}") #?subvectors127print(f"v1 * 2 = {v1Vector(3, 4) * 2}") #?mulscalar128print(f"3 * v2 = {3 * v2}") #?rmulscalar
    outputv1 - v2 = Vector(2, 2)
  9. def __mul__(self, scalar): #?vectormul

    pass 1 of 2
    21def __mul__(selfVector(3, 4), scalar2): #?vectormul22    """v * scalar"""23    return Vector(self.x3 * scalar2, self.y4 * scalar) #?mulreturn
  10. print(f"v1 * 2 = {v1 * 2}") #?mulscalar

    126print(f"v1 - v2 = {v1 - v2}") #?subvectors127print(f"v1 * 2 = {v1Vector(3, 4) * 2}") #?mulscalar128print(f"3 * v2 = {3 * v2Vector(1, 2)}") #?rmulscalar129print(f"|v1| = {abs(v1):.2f}") #?absvector
    outputv1 * 2 = Vector(6, 8)
  11. def __rmul__(self, scalar): #?vectorrmul

    25def __rmul__(selfVector(1, 2), scalar3): #?vectorrmul26    """scalar * v (reversed)"""27    return self.__mul__(scalar3) #?rmuldelegate
  12. def __mul__(self, scalar): #?vectormul

    pass 2 of 2
    21def __mul__(selfVector(1, 2), scalar3): #?vectormul22    """v * scalar"""23    return Vector(self.x1 * scalar3, self.y2 * scalar) #?mulreturn
  13. print(f"3 * v2 = {3 * v2}") #?rmulscalar

    127print(f"v1 * 2 = {v1 * 2}") #?mulscalar128print(f"3 * v2 = {3 * v2Vector(1, 2)}") #?rmulscalar129print(f"|v1| = {abs(v1Vector(3, 4)):.2f}") #?absvector130print(f"v1 == v2: {v1 == v2}") #?eqvectors
    output3 * v2 = Vector(3, 6)
  14. def __abs__(self): #?vectorabs

    33def __abs__(selfVector(3, 4)): #?vectorabs34    """abs(v) - magnitude"""35    return (self.x3 ** 2 + self.y4 ** 2) ** 0.5 #?magnitude
  15. print(f"|v1| = {abs(v1):.2f}") #?absvector

    128print(f"3 * v2 = {3 * v2}") #?rmulscalar129print(f"|v1| = {abs(v1Vector(3, 4)):.2f}") #?absvector130print(f"v1 == v2: {v1Vector(3, 4) == v2Vector(1, 2)}") #?eqvectors131print(f"v1 == Vector(3, 4): {v1 == Vector(3, 4)}") #?eqsame
    output|v1| = 5.00
  16. def __eq__(self, other): #?vectoreq

    pass 1 of 2
    29def __eq__(selfVector(3, 4), otherVector(1, 2)): #?vectoreq30    """v1 == v2"""31    return self.x3 == other.x1 and self.y4 == other.y2 #?eqreturn
  17. print(f"v1 == v2: {v1 == v2}") #?eqvectors

    129print(f"|v1| = {abs(v1):.2f}") #?absvector130print(f"v1 == v2: {v1Vector(3, 4) == v2Vector(1, 2)}") #?eqvectors131print(f"v1 == Vector(3, 4): {v1Vector(3, 4) == Vector(3, 4)}") #?eqsame
    outputv1 == v2: False
  18. def __eq__(self, other): #?vectoreq

    pass 2 of 2
    29def __eq__(selfVector(3, 4), otherVector(3, 4)): #?vectoreq30    """v1 == v2"""31    return self.x3 == other.x3 and self.y4 == other.y4 #?eqreturn
  19. print(f"v1 == Vector(3, 4): {v1 == Vector(3, 4)}") #?eqsame

    130print(f"v1 == v2: {v1 == v2}") #?eqvectors131print(f"v1 == Vector(3, 4): {v1Vector(3, 4) == Vector(3, 4)}") #?eqsame132133# Money operations #?moneydemo134print("\n--- Money Operations ---")135price = Money(19, 99) #?createprice136tax = Money(1, 60) #?createtax
    outputv1 == Vector(3, 4): True
    
    --- Money Operations ---
  20. total_cents ← 1999, self.dollars ← 19, self.cents ← 99

    pass 1 of 7
    41def __init__(self(empty), dollars19, cents99=0): #?moneyinit42    total_cents→ 1999 = dollars19 * 100 + cents99 #?totalcents43    self.dollars→ 19 = total_cents1999 // 100 #?normalize44    self.cents→ 99 = total_cents1999 % 100
    All 7 passes — pass 1 is the card above
    passdollarscentsotherother.dollarsother.centstotal_centsself.dollarsself.cents
    1199919991999
    2160160160
    35050050
    4215921592159
    5165916591659
    6399839983998
    71999$19.99199919991999
  21. price ← $19.99

    134print("\n--- Money Operations ---")135price→ $19.99 = Money(19, 99) #?createprice136tax = Money(1, 60) #?createtax137discount = Money(5, 0) #?creatediscount
  22. tax ← $1.60

    135price = Money(19, 99) #?createprice136tax→ $1.60 = Money(1, 60) #?createtax137discount = Money(5, 0) #?creatediscount
  23. discount ← $5.00

    136tax = Money(1, 60) #?createtax137discount→ $5.00 = Money(5, 0) #?creatediscount138139print(f"Price: {price$19.99}")140print(f"Tax: {tax$1.60}")141print(f"Discount: {discount$5.00}")142143total = price$19.99 + tax$1.60 #?addmoney144print(f"Price + Tax = {total}")
    outputPrice: $19.99
    Tax: $1.60
    Discount: $5.00
  24. total_cents ← 2159

    52def __add__(self$19.99, other$1.60): #?moneyadd53    """m1 + m2"""54    total_cents→ 2159 = (self.dollars19 * 100 + self.cents99 + 55                  other.dollars1 * 100 + other.cents60)56    return Money(total_cents2159 // 100, total_cents % 100)
  25. total ← $21.59

    143total→ $21.59 = price$19.99 + tax$1.60 #?addmoney144print(f"Price + Tax = {total$21.59}")145146final = total$21.59 - discount$5.00 #?submoney147print(f"After discount = {final}")
    outputPrice + Tax = $21.59
  26. total_cents ← 1659

    58def __sub__(self$21.59, other$5.00): #?moneysub59    """m1 - m2"""60    total_cents→ 1659 = (self.dollars21 * 100 + self.cents59 - 61                  other.dollars5 * 100 - other.cents0)62    return Money(total_cents1659 // 100, total_cents % 100)
  27. final ← $16.59

    146final→ $16.59 = total$21.59 - discount$5.00 #?submoney147print(f"After discount = {final$16.59}")148149doubled = price$19.99 * 2 #?mulmoney150print(f"Price * 2 = {doubled}")
    outputAfter discount = $16.59
  28. total_cents ← 3998

    64def __mul__(self$19.99, factor2): #?moneymul65    """m * factor"""66    total_cents→ 3998 = int((self.dollars19 * 100 + self.cents99) * factor2)67    return Money(total_cents3998 // 100, total_cents % 100)
  29. doubled ← $39.98

    149doubled→ $39.98 = price$19.99 * 2 #?mulmoney150print(f"Price * 2 = {doubled$39.98}")151152print(f"Tax < Discount: {tax$1.60 < discount$5.00}") #?comparemoney153print(f"Price == Money(19, 99): {price == Money(19, 99)}") #?eqmoney
    outputPrice * 2 = $39.98
  30. def __lt__(self, other): #?moneylt

    72def __lt__(self$1.60, other$5.00): #?moneylt73    """m1 < m2"""74    return (self.dollars1 * 100 + self.cents60) < (other.dollars5 * 100 + other.cents0)
  31. print(f"Tax < Discount: {tax < discount}") #?comparemoney

    152print(f"Tax < Discount: {tax$1.60 < discount$5.00}") #?comparemoney153print(f"Price == Money(19, 99): {price$19.99 == Money(19, 99)}") #?eqmoney
    outputTax < Discount: True
  32. def __eq__(self, other): #?moneyeq

    80def __eq__(self$19.99, other$19.99): #?moneyeq81    """m1 == m2"""82    return self.dollars19 == other.dollars19 and self.cents99 == other.cents99
  33. print(f"Price == Money(19, 99): {price == Money(19, 99)}") #?eqmoney

    152print(f"Tax < Discount: {tax < discount}") #?comparemoney153print(f"Price == Money(19, 99): {price$19.99 == Money(19, 99)}") #?eqmoney154155# StringList operations #?stringlistdemo156print("\n--- StringList Operations ---")157fruits = StringList("apple", "banana") #?createfruits158veggies = StringList("carrot", "broccoli") #?createveggies
    outputPrice == Money(19, 99): True
    
    --- StringList Operations ---
  34. self.items ← ['apple', 'banana']

    pass 1 of 3
    88def __init__(self(empty), *items('apple', 'banana')): #?stringlistinit89    self.items→ ['apple', 'banana'] = list(items('apple', 'banana'))
    All 3 passes — pass 1 is the card above
    passitemsself.items
    1('apple', 'banana')['apple', 'banana']
    2('carrot', 'broccoli')['carrot', 'broccoli']
    3('apple', 'banana', 'carrot', 'broccoli')['apple', 'banana', 'carrot', 'broccoli']
  35. fruits ← StringList('apple', 'banana')

    156print("\n--- StringList Operations ---")157fruits→ StringList('apple', 'banana') = StringList("apple", "banana") #?createfruits158veggies = StringList("carrot", "broccoli") #?createveggies
  36. veggies ← StringList('carrot', 'broccoli')

    157fruits = StringList("apple", "banana") #?createfruits158veggies→ StringList('carrot', 'broccoli') = StringList("carrot", "broccoli") #?createveggies159160print(f"fruits = {fruitsStringList('apple', 'banana')}")161print(f"veggies = {veggiesStringList('carrot', 'broccoli')}")162163combined = fruitsStringList('apple', 'banana') + veggiesStringList('carrot', 'broccoli') #?addlists164print(f"combined = {combined}")
    outputfruits = StringList('apple', 'banana')
    veggies = StringList('carrot', 'broccoli')
  37. def __add__(self, other): #?stringlistadd

    94def __add__(selfStringList('apple', 'banana'), otherStringList('carrot', 'broccoli')): #?stringlistadd95    """Concatenate two StringLists"""96    return StringList(*(self.items['apple', 'banana'] + other.items['carrot', 'broccoli'])) #?concatreturn
  38. combined ← StringList('apple', 'banana', 'carrot', 'broccoli')

    163combined→ StringList('apple', 'banana', 'carrot', 'broccoli') = fruitsStringList('apple', 'banana') + veggiesStringList('carrot', 'broccoli') #?addlists164print(f"combined = {combinedStringList('apple', 'banana', 'carrot', 'broccoli')}")165166print(f"len(combined) = {len(combinedStringList('apple', 'banana', 'carrot', 'broccoli'))}") #?lencombined167print(f"combined[0] = {combined[0]}") #?indexcombined
    outputcombined = StringList('apple', 'banana', 'carrot', 'broccoli')
  39. def __len__(self): #?stringlistlen

    98def __len__(selfStringList('apple', 'banana', 'carrot', 'broccoli')): #?stringlistlen99    """len(sl)"""100    return len(self.items['apple', 'banana', 'carrot', 'broccoli'])
  40. print(f"len(combined) = {len(combined)}") #?lencombined

    166print(f"len(combined) = {len(combinedStringList('apple', 'banana', 'carrot', 'broccoli'))}") #?lencombined167print(f"combined[0] = {combined[0]apple}") #?indexcombined168print(f"'apple' in combined: {'apple' in combined}") #?incombined
    outputlen(combined) = 4
  41. def __getitem__(self, index): #?stringlistget

    102def __getitem__(selfStringList('apple', 'banana', 'carrot', 'broccoli'), index0): #?stringlistget103    """sl[index]"""104    return self.items[index]apple #?getreturn
  42. print(f"combined[0] = {combined[0]}") #?indexcombined

    166print(f"len(combined) = {len(combined)}") #?lencombined167print(f"combined[0] = {combined[0]apple}") #?indexcombined168print(f"'apple' in combined: {'apple' in combinedStringList('apple', 'banana', 'carrot', 'broccoli')}") #?incombined
    outputcombined[0] = apple
  43. def __contains__(self, item): #?stringlistcontains

    106def __contains__(selfStringList('apple', 'banana', 'carrot', 'broccoli'), itemapple): #?stringlistcontains107    """item in sl"""108    return itemapple in self.items['apple', 'banana', 'carrot', 'broccoli'] #?containsreturn
  44. print(f"'apple' in combined: {'apple' in combined}") #?incombined

    167print(f"combined[0] = {combined[0]}") #?indexcombined168print(f"'apple' in combined: {'apple' in combinedStringList('apple', 'banana', 'carrot', 'broccoli')}") #?incombined169170print("Iterating:")171for item in combined: #?iteratecombined
    output'apple' in combined: True
    Iterating:
  45. def __iter__(self): #?stringlistiter

    110def __iter__(selfStringList('apple', 'banana', 'carrot', 'broccoli')): #?stringlistiter111    """for item in sl"""112    return iter(self.items['apple', 'banana', 'carrot', 'broccoli']) #?iterreturn
  46. for item in combined: #?iteratecombined

    pass 1 of 4
    170print("Iterating:")171for itemapple in combinedStringList('apple', 'banana', 'carrot', 'broccoli'): #?iteratecombined172    print(f"  - {itemapple}")
    output  - apple
    All 4 passes — pass 1 is the card above
    passitem
    1apple
    2banana
    3carrot
    4broccoli
  47. print(" === Common Operators ===")

    174print("\n=== Common Operators ===")175print("""176Arithmetic:177  __add__(self, other)    +178  __sub__(self, other)    -179  __mul__(self, other)    *180  __truediv__(self, other) /181  __rmul__(self, other)   reversed * (scalar * obj)182183Comparison:184  __eq__(self, other)     ==185  __ne__(self, other)     !=186  __lt__(self, other)     <187  __le__(self, other)     <=188  __gt__(self, other)     >189  __ge__(self, other)     >=190191Container:192  __len__(self)           len(obj)193  __getitem__(self, key)  obj[key]194  __contains__(self, item) item in obj195  __iter__(self)          for x in obj196197Other:198  __repr__(self)          repr(obj), print in shell199  __str__(self)           str(obj), print()200  __abs__(self)           abs(obj)201  __bool__(self)          bool(obj), if obj202""")
    output
    === Common Operators ===
    
    Arithmetic:
      __add__(self, other)    +
      __sub__(self, other)    -
      __mul__(self, other)    *
      __truediv__(self, other) /
      __rmul__(self, other)   reversed * (scalar * obj)
    
    Comparison:
      __eq__(self, other)     ==
      __ne__(self, other)     !=
      __lt__(self, other)     <
      __le__(self, other)     <=
      __gt__(self, other)     >
      __ge__(self, other)     >=
    
    Container:
      __len__(self)           len(obj)
      __getitem__(self, key)  obj[key]
      __contains__(self, item) item in obj
      __iter__(self)          for x in obj
    
    Other:
      __repr__(self)          repr(obj), print in shell
      __str__(self)           str(obj), print()
      __abs__(self)           abs(obj)
      __bool__(self)          bool(obj), if obj

Define __add__, __eq__, etc. to make operators work with your class.

operator overloading Define special methods to customize operator behavior for your class.

Exercise: practical.py

Build a payment system with polymorphic processors