OOP Intermediate
Polymorphism
Same Interface, Different Behavior
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 - "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
""")
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⟩] #?quackerslistoutput=== 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 ---for obj in quackers: #?iteratequackers
pass 1 of 371for obj⟨Duck A⟩ in quackers[⟨Duck A⟩, ⟨Person B⟩, ⟨RobotDuck C⟩]: #?iteratequackers72 print(f"{type(obj⟨Duck A⟩).__name__}: {make_it_quack(obj)}") #?printquackAll 3 passes — pass 1 is the card above pass objself1 ⟨Duck A⟩ ⟨Duck A⟩ 2 ⟨Person B⟩ ⟨Person B⟩ 3 ⟨RobotDuck C⟩ ⟨RobotDuck C⟩ def make_it_quack(duck_like_thing): #?makeitquack
pass 1 of 443# 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() #?callquackAll 4 passes — pass 1 is the card above pass duck_like_thingselfe1 ⟨Duck A⟩ ⟨Duck A⟩ — 2 ⟨Person B⟩ ⟨Person B⟩ — 3 ⟨RobotDuck C⟩ ⟨RobotDuck C⟩ — 4 ⟨Dog D⟩ — 'Dog' object has no attribute 'quack' def quack(self): #?duckquack
pass 1 of 26def quack(self⟨Duck A⟩): #?duckquack7 return "Quack quack!"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)}") #?printquackoutputDuck: Quack quack!def quack(self): #?personquack
pass 1 of 216def quack(self⟨Person B⟩): #?personquack17 return "I'm pretending to quack!"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)}") #?printquackoutputPerson: I'm pretending to quack!def quack(self): #?robotquack
pass 1 of 226def quack(self⟨RobotDuck C⟩): #?robotquack27 return "Electronic quack!"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)}") #?printquackoutputRobotDuck: Electronic quack!print(" --- Full duck demonstration ---")
74print("\n--- Full duck demonstration ---")output --- Full duck demonstration ---for obj in quackers:
pass 1 of 376for 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⟩) #?callduckshowoutput Duck:All 3 passes — pass 1 is the card above pass objself1 ⟨Duck A⟩ ⟨Duck A⟩ 2 ⟨Person B⟩ ⟨Person B⟩ 3 ⟨RobotDuck C⟩ ⟨RobotDuck C⟩ def duck_show(duck_like_thing): #?duckshow
pass 1 of 349def 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 pass duck_like_thingself1 ⟨Duck A⟩ ⟨Duck A⟩ 2 ⟨Person B⟩ ⟨Person B⟩ 3 ⟨RobotDuck C⟩ ⟨RobotDuck C⟩ def quack(self): #?duckquack
pass 2 of 26def quack(self⟨Duck A⟩): #?duckquack7 return "Quack quack!"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!def walk(self): #?duckwalk
9def walk(self⟨Duck A⟩): #?duckwalk10 return "Waddle waddle"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 waddleduck_show(obj) #?callduckshow
77print(f"\n{type(obj).__name__}:")78duck_show(obj⟨Duck A⟩) #?callduckshowdef quack(self): #?personquack
pass 2 of 216def quack(self⟨Person B⟩): #?personquack17 return "I'm pretending to quack!"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!def walk(self): #?personwalk
19def walk(self⟨Person B⟩): #?personwalk20 return "Walking on two legs"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 legsduck_show(obj) #?callduckshow
77print(f"\n{type(obj).__name__}:")78duck_show(obj⟨Person B⟩) #?callduckshowdef quack(self): #?robotquack
pass 2 of 226def quack(self⟨RobotDuck C⟩): #?robotquack27 return "Electronic quack!"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!def walk(self): #?robotwalk
29def walk(self⟨RobotDuck C⟩): #?robotwalk30 return "Mechanical walking..."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...duck_show(obj) #?callduckshow
77print(f"\n{type(obj).__name__}:")78duck_show(obj⟨RobotDuck C⟩) #?callduckshowdog ← ⟨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...") #?dogcantquackoutput --- What about Dog? ---def walk(self):
39def walk(self⟨Dog D⟩):40 return "Running on four legs"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...") #?dogcantquackoutputDog can walk: Running on four legs But if we try to make Dog quack...try:
86try:87 make_it_quack(dog⟨Dog D⟩) #?tryquackdog88except AttributeError as e: #?attributeerrorexcept 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!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().
Method override polymorphism
Same method name, different behavior per class.
# 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)
""")
"""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 ===self.name ← Buddy
pass 1 of 76def __init__(self⟨Dog A⟩, nameBuddy): #?animalinit7 self.name→ Buddy = nameBuddyAll 7 passes — pass 1 is the card above pass selfnameself.name1 ⟨Dog A⟩ Buddy Buddy 2 ⟨Cat B⟩ Whiskers Whiskers 3 ⟨Cow C⟩ Bessie Bessie 4 ⟨Fish D⟩ Nemo Nemo 5 ⟨Dog E⟩ Max Max 6 ⟨Cat F⟩ Mittens Mittens 7 ⟨Duck G⟩ Donald Donald 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⟩]) #?callconcertdef 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: #?iterateanimalsoutput** Animal Concert ** ------------------------------for animal in animals: #?iterateanimals
pass 1 of 661print("-" * 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 pass animalselfself.name1 ⟨Dog A⟩ — — 2 ⟨Cat B⟩ — — 3 ⟨Cow C⟩ ⟨Cow C⟩ Bessie 4 ⟨Fish D⟩ ⟨Fish D⟩ Nemo 5 ⟨Dog E⟩ — — 6 ⟨Cat F⟩ — — def speak(self): #?dogspeak
pass 1 of 420def speak(self⟨Dog A⟩): #?dogspeak21 return f"{self.nameBuddy} says: Woof woof!"All 4 passes — pass 1 is the card above pass selfself.name1 ⟨Dog A⟩ Buddy 2 ⟨Dog E⟩ Max 3 ⟨Dog A⟩ Buddy 4 ⟨Dog E⟩ Max print(animal.speak()) #?callspeak
62for animal in animals: #?iterateanimals63 print(animal⟨Dog A⟩.speak()) #?callspeak64print("-" * 30)outputBuddy says: Woof woof!def speak(self): #?catspeak
pass 1 of 430def speak(self⟨Cat B⟩): #?catspeak31 return f"{self.nameWhiskers} says: Meow~"All 4 passes — pass 1 is the card above pass selfself.name1 ⟨Cat B⟩ Whiskers 2 ⟨Cat F⟩ Mittens 3 ⟨Cat B⟩ Whiskers 4 ⟨Cat F⟩ Mittens print(animal.speak()) #?callspeak
62for animal in animals: #?iterateanimals63 print(animal⟨Cat B⟩.speak()) #?callspeak64print("-" * 30)outputWhiskers says: Meow~def speak(self): #?cowspeak
pass 1 of 240def speak(self⟨Cow C⟩): #?cowspeak41 return f"{self.nameBessie} says: Moo!"print(animal.speak()) #?callspeak
62for animal in animals: #?iterateanimals63 print(animal⟨Cow C⟩.speak()) #?callspeak64print("-" * 30)outputBessie says: Moo!def speak(self): #?fishspeak
pass 1 of 250def speak(self⟨Fish D⟩): #?fishspeak51 return f"{self.nameNemo} says: ... (bubbles)"print(animal.speak()) #?callspeak
62for animal in animals: #?iterateanimals63 print(animal⟨Fish D⟩.speak()) #?callspeak64print("-" * 30)outputNemo says: ... (bubbles)print(animal.speak()) #?callspeak
62for animal in animals: #?iterateanimals63 print(animal⟨Dog E⟩.speak()) #?callspeak64print("-" * 30)outputMax says: Woof woof!print(animal.speak()) #?callspeak
62for animal in animals: #?iterateanimals63 print(animal⟨Cat F⟩.speak()) #?callspeak64print("-" * 30)outputMittens says: Meow~print("-" * 30)
63 print(animal.speak()) #?callspeak64print("-" * 30)output------------------------------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⟩]) #?callrollcalldef 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:for animal in animals:
pass 1 of 669print("\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()}") #?calldescribeAll 6 passes — pass 1 is the card above pass animalselfself.name1 ⟨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 def describe(self):
pass 1 of 223def describe(self⟨Dog A⟩):24 return f"{self.nameBuddy} is a loyal dog"print(f" - {animal.describe()}") #?calldescribe
70for animal in animals:71 print(f" - {animal⟨Dog A⟩.describe()}") #?calldescribeoutput - Buddy is a loyal dogdef describe(self):
pass 1 of 233def describe(self⟨Cat B⟩):34 return f"{self.nameWhiskers} is an independent cat"print(f" - {animal.describe()}") #?calldescribe
70for animal in animals:71 print(f" - {animal⟨Cat B⟩.describe()}") #?calldescribeoutput - Whiskers is an independent catdef describe(self):
43def describe(self⟨Cow C⟩):44 return f"{self.nameBessie} is a gentle cow"print(f" - {animal.describe()}") #?calldescribe
70for animal in animals:71 print(f" - {animal⟨Cow C⟩.describe()}") #?calldescribeoutput - Bessie is a gentle cowdef describe(self):
53def describe(self⟨Fish D⟩):54 return f"{self.nameNemo} is a quiet fish"print(f" - {animal.describe()}") #?calldescribe
70for animal in animals:71 print(f" - {animal⟨Fish D⟩.describe()}") #?calldescribeoutput - Nemo is a quiet fishdef describe(self):
pass 2 of 223def describe(self⟨Dog E⟩):24 return f"{self.nameMax} is a loyal dog"print(f" - {animal.describe()}") #?calldescribe
70for animal in animals:71 print(f" - {animal⟨Dog E⟩.describe()}") #?calldescribeoutput - Max is a loyal dogdef describe(self):
pass 2 of 233def describe(self⟨Cat F⟩):34 return f"{self.nameMittens} is an independent cat"print(f" - {animal.describe()}") #?calldescribe
70for animal in animals:71 print(f" - {animal⟨Cat F⟩.describe()}") #?calldescribeoutput - Mittens is an independent catanimal_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 ---for animal in animals: #?uniformloop # All animals have same inter…
pass 1 of 694for 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()) #?polymorphiccalloutputBuddy:All 6 passes — pass 1 is the card above pass animalanimal.nameselfself.name1 ⟨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 — — print(animal.speak()) #?polymorphiccall
96print(f"{animal.name}: ", end="")97print(animal⟨Dog A⟩.speak()) #?polymorphiccalloutputBuddy says: Woof woof!print(animal.speak()) #?polymorphiccall
96print(f"{animal.name}: ", end="")97print(animal⟨Cat B⟩.speak()) #?polymorphiccalloutputWhiskers says: Meow~def speak(self): #?cowspeak
pass 2 of 240def speak(self⟨Cow C⟩): #?cowspeak41 return f"{self.nameBessie} says: Moo!"print(animal.speak()) #?polymorphiccall
96print(f"{animal.name}: ", end="")97print(animal⟨Cow C⟩.speak()) #?polymorphiccalloutputBessie says: Moo!def speak(self): #?fishspeak
pass 2 of 250def speak(self⟨Fish D⟩): #?fishspeak51 return f"{self.nameNemo} says: ... (bubbles)"print(animal.speak()) #?polymorphiccall
96print(f"{animal.name}: ", end="")97print(animal⟨Fish D⟩.speak()) #?polymorphiccalloutputNemo says: ... (bubbles)print(animal.speak()) #?polymorphiccall
96print(f"{animal.name}: ", end="")97print(animal⟨Dog E⟩.speak()) #?polymorphiccalloutputMax says: Woof woof!print(animal.speak()) #?polymorphiccall
96print(f"{animal.name}: ", end="")97print(animal⟨Cat F⟩.speak()) #?polymorphiccalloutputMittens says: Meow~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 ---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()}")def describe(self):
106def describe(self⟨Duck G⟩):107 return f"{self.nameDonald} is a happy duck"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 duckdef speak(self): #?duckspeak
102class Duck(Animal): #?duckclass103 def speak(self⟨Duck G⟩): #?duckspeak104 return f"{self.nameDonald} says: Quack quack!"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.
# 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.
Polymorphic functions
Write functions that work with any compatible object.
# 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
""")
print("=== Polymorphic Functions === ")
98print("=== Polymorphic Functions ===\n")99100# Create different objects #?createobjects101dog = Dog("Buddy") #?createdog102cat = Cat("Whiskers") #?createcatoutput=== Polymorphic Functions ===self.name ← Buddy
3class Dog: #?dogclass4 def __init__(self⟨Dog A⟩, nameBuddy):5 self.name→ Buddy = nameBuddydog ← ⟨Dog A⟩
100# Create different objects #?createobjects101dog→ ⟨Dog A⟩ = Dog("Buddy") #?createdog102cat = Cat("Whiskers") #?createcat103robot = Robot("R2D2") #?createrobotself.name ← Whiskers
14class Cat: #?catclass15 def __init__(self⟨Cat B⟩, nameWhiskers):16 self.name→ Whiskers = nameWhiskerscat ← ⟨Cat B⟩
101dog = Dog("Buddy") #?createdog102cat→ ⟨Cat B⟩ = Cat("Whiskers") #?createcat103robot = Robot("R2D2") #?createrobotself.model ← R2D2
25class Robot: #?robotclass26 def __init__(self⟨Robot C⟩, modelR2D2):27 self.model→ R2D2 = modelR2D2robot ← ⟨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) ---for s in speakers:
pass 1 of 3109print("--- make_speak (basic polymorphism) ---")110for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Cat B⟩, ⟨Robot C⟩]:111 print(f" {make_speak(s⟨Dog A⟩)}") #?callmakespeakAll 3 passes — pass 1 is the card above pass s1 ⟨Dog A⟩ 2 ⟨Cat B⟩ 3 ⟨Robot C⟩ def make_speak(speaker): #?makespeak
pass 1 of 936# 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() #?callspeakAll 9 passes — pass 1 is the card above pass speaker1 ⟨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⟩ def speak(self):
pass 1 of 77def speak(self⟨Dog A⟩):8 return "Woof!"print(f" {make_speak(s)}") #?callmakespeak
110for s in speakers:111 print(f" {make_speak(s⟨Dog A⟩)}") #?callmakespeakoutput Woof!def speak(self):
pass 1 of 718def speak(self⟨Cat B⟩):19 return "Meow!"print(f" {make_speak(s)}") #?callmakespeak
110for s in speakers:111 print(f" {make_speak(s⟨Cat B⟩)}") #?callmakespeakoutput Meow!def speak(self):
pass 1 of 729def speak(self⟨Robot C⟩):30 return "Beep boop!"print(f" {make_speak(s)}") #?callmakespeak
110for s in speakers:111 print(f" {make_speak(s⟨Robot C⟩)}") #?callmakespeakoutput Beep boop!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) ---for s in speakers:
pass 1 of 3114print("\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⟩)}") #?calldescribeAll 3 passes — pass 1 is the card above pass s1 ⟨Dog A⟩ 2 ⟨Cat B⟩ 3 ⟨Robot C⟩ def describe_speaker(speaker): #?describespeaker
pass 1 of 345# 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 pass speaker1 ⟨Dog A⟩ 2 ⟨Cat B⟩ 3 ⟨Robot C⟩ sound ← Woof!
49"""50sound→ Woof! = speaker⟨Dog A⟩.speak()identifier ← Buddy
pass 1 of 252# Can use hasattr to check for attributes #?hasattr53if hasattr(speaker⟨Dog A⟩, 'name'): #?checkname54 identifier→ Buddy = speaker.nameBuddy55elif hasattr(speaker, 'model'): #?checkmodelreturn f"{identifier} says: {sound}"
60return f"{identifierBuddy} says: {soundWoof!}"print(f" {describe_speaker(s)}") #?calldescribe
115for s in speakers:116 print(f" {describe_speaker(s⟨Dog A⟩)}") #?calldescribeoutput Buddy says: Woof!sound ← Meow!
49"""50sound→ Meow! = speaker⟨Cat B⟩.speak()identifier ← Whiskers
pass 2 of 252# Can use hasattr to check for attributes #?hasattr53if hasattr(speaker⟨Cat B⟩, 'name'): #?checkname54 identifier→ Whiskers = speaker.nameWhiskers55elif hasattr(speaker, 'model'): #?checkmodelreturn f"{identifier} says: {sound}"
60return f"{identifierWhiskers} says: {soundMeow!}"print(f" {describe_speaker(s)}") #?calldescribe
115for s in speakers:116 print(f" {describe_speaker(s⟨Cat B⟩)}") #?calldescribeoutput Whiskers says: Meow!sound ← Beep boop!
49"""50sound→ Beep boop! = speaker⟨Robot C⟩.speak()identifier ← R2D2
54 identifier = speaker.name55elif hasattr(speaker⟨Robot C⟩, 'model'): #?checkmodel56 identifier→ R2D2 = speaker.modelR2D257else:return f"{identifier} says: {sound}"
60return f"{identifierR2D2} says: {soundBeep boop!}"print(f" {describe_speaker(s)}") #?calldescribe
115for s in speakers:116 print(f" {describe_speaker(s⟨Robot C⟩)}") #?calldescribeoutput R2D2 says: Beep boop!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) ---for s in speakers:
pass 1 of 3119print("\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⟩)}") #?callshowlenAll 3 passes — pass 1 is the card above pass sselfself.nameself.model1 ⟨Dog A⟩ ⟨Dog A⟩ Buddy — 2 ⟨Cat B⟩ ⟨Cat B⟩ Whiskers — 3 ⟨Robot C⟩ ⟨Robot C⟩ — R2D2 def show_length(obj): #?showlength
pass 1 of 663# 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)}" #?calllenAll 6 passes — pass 1 is the card above pass objselfself.nameself.model1 ⟨Dog A⟩ ⟨Dog A⟩ Buddy — 2 ⟨Cat B⟩ ⟨Cat B⟩ Whiskers — 3 ⟨Robot C⟩ ⟨Robot C⟩ — R2D2 4 Hello — — — 5 [1, 2, 3] — — — 6 {'a': 1, 'b': 2} — — — def __len__(self): #?doglen
10def __len__(self⟨Dog A⟩): #?doglen11 return len(self.nameBuddy)print(f" {show_length(s)}") #?callshowlen
120for s in speakers:121 print(f" {show_length(s⟨Dog A⟩)}") #?callshowlenoutput Length of Dog: 5def __len__(self):
21def __len__(self⟨Cat B⟩):22 return len(self.nameWhiskers)print(f" {show_length(s)}") #?callshowlen
120for s in speakers:121 print(f" {show_length(s⟨Cat B⟩)}") #?callshowlenoutput Length of Cat: 8def __len__(self):
32def __len__(self⟨Robot C⟩):33 return len(self.modelR2D2)print(f" {show_length(s)}") #?callshowlen
120for s in speakers:121 print(f" {show_length(s⟨Robot C⟩)}") #?callshowlenoutput Length of Robot: 4print(" 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])}") #?listlenoutput Also works with built-in types: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})}") #?dictlenoutput Length of str: 5print(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})}") #?dictlenoutput Length of list: 3make_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⟩]) #?callchorusoutput Length of dict: 2def 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): #?repeattimesoutput ** Chorus Time! **for _ in range(times): #?repeattimes
pass 1 of 278print("\n** Chorus Time! **")79for _0 in range(times2): #?repeattimes80 for speaker in speakers: #?iteratespeakers81 print(f" {make_speak(speaker)}") #?delegatespeakfor speaker in speakers: #?iteratespeakers
pass 1 of 679for _ 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 pass speaker1 ⟨Dog A⟩ 2 ⟨Cat B⟩ 3 ⟨Robot C⟩ 4 ⟨Dog A⟩ 5 ⟨Cat B⟩ 6 ⟨Robot C⟩ print(f" {make_speak(speaker)}") #?delegatespeak
80for speaker in speakers: #?iteratespeakers81 print(f" {make_speak(speaker⟨Dog A⟩)}") #?delegatespeak82print("---")output Woof!print(f" {make_speak(speaker)}") #?delegatespeak
80for speaker in speakers: #?iteratespeakers81 print(f" {make_speak(speaker⟨Cat B⟩)}") #?delegatespeak82print("---")output Meow!print(f" {make_speak(speaker)}") #?delegatespeak
80for speaker in speakers: #?iteratespeakers81 print(f" {make_speak(speaker⟨Robot C⟩)}") #?delegatespeak82print("---")output Beep boop!print("---")
81 print(f" {make_speak(speaker)}") #?delegatespeak82print("---")output---for _ in range(times): #?repeattimes
pass 2 of 278print("\n** Chorus Time! **")79for _1 in range(times2): #?repeattimes80 for speaker in speakers: #?iteratespeakers81 print(f" {make_speak(speaker)}") #?delegatespeakprint(f" {make_speak(speaker)}") #?delegatespeak
80for speaker in speakers: #?iteratespeakers81 print(f" {make_speak(speaker⟨Dog A⟩)}") #?delegatespeak82print("---")output Woof!print(f" {make_speak(speaker)}") #?delegatespeak
80for speaker in speakers: #?iteratespeakers81 print(f" {make_speak(speaker⟨Cat B⟩)}") #?delegatespeak82print("---")output Meow!print(f" {make_speak(speaker)}") #?delegatespeak
80for speaker in speakers: #?iteratespeakers81 print(f" {make_speak(speaker⟨Robot C⟩)}") #?delegatespeak82print("---")output Beep boop!print("---")
81 print(f" {make_speak(speaker)}") #?delegatespeak82print("---")output---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:results ← []
pass 1 of 285# 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:for speaker in speakers:
pass 1 of 691results = []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 pass speaker1 ⟨Dog A⟩ 2 ⟨Cat B⟩ 3 ⟨Robot C⟩ 4 ⟨Dog A⟩ 5 ⟨Cat B⟩ 6 ⟨Robot C⟩ def loud_speak(s): #?loudspeak
pass 1 of 3135# Different processors #?processors136def loud_speak(s⟨Dog A⟩): #?loudspeak137 return s⟨Dog A⟩.speak().upper() + "!!"All 3 passes — pass 1 is the card above pass s1 ⟨Dog A⟩ 2 ⟨Cat B⟩ 3 ⟨Robot C⟩ result ← WOOF!!!, results ← ['WOOF!!!']
92for speaker in speakers:93 result→ WOOF!!! = processor(speaker⟨Dog A⟩) #?callprocessor94 results→ ['WOOF!!!'].append(resultWOOF!!!)95return resultsresult ← MEOW!!!, results ← ['WOOF!!!', 'MEOW!!!']
92for speaker in speakers:93 result→ MEOW!!! = processor(speaker⟨Cat B⟩) #?callprocessor94 results→ ['WOOF!!!', 'MEOW!!!'].append(resultMEOW!!!)95return resultsresult ← 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!!!']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:for r in results:
pass 1 of 3143results = 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 pass r1 WOOF!!! 2 MEOW!!! 3 BEEP BOOP!!! 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:results ← []
pass 2 of 285# 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:def count_speak(s): #?countspeak
pass 1 of 3139def 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 pass s1 ⟨Dog A⟩ 2 ⟨Cat B⟩ 3 ⟨Robot C⟩ 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 resultsresult ← 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 resultsresult ← 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)']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:for r in results:
pass 1 of 3148results = 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 pass r1 Woof! (5 chars) 2 Meow! (5 chars) 3 Beep boop! (10 chars) 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
print("=== Polymorphic Functions === ")
98print("=== Polymorphic Functions ===\n")99100# Create different objects101dog = Dog("Buddy")102cat = Cat("Whiskers")output=== Polymorphic Functions ===self.name ← Buddy
3class Dog:4 def __init__(self⟨Dog A⟩, nameBuddy):5 self.name→ Buddy = nameBuddydog ← ⟨Dog A⟩
100# Create different objects101dog→ ⟨Dog A⟩ = Dog("Buddy")102cat = Cat("Whiskers")103robot = Robot("R2D2")self.name ← Whiskers
14class Cat:15 def __init__(self⟨Cat B⟩, nameWhiskers):16 self.name→ Whiskers = nameWhiskerscat ← ⟨Cat B⟩
101dog = Dog("Buddy")102cat→ ⟨Cat B⟩ = Cat("Whiskers")103robot = Robot("R2D2")self.model ← R2D2
25class Robot:26 def __init__(self⟨Robot C⟩, modelR2D2):27 self.model→ R2D2 = modelR2D2robot ← ⟨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) ---for s in speakers:
pass 1 of 2108print("--- make_speak (basic polymorphism) ---")109for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:110 print(f" {make_speak(s⟨Dog A⟩)}")def make_speak(speaker):
pass 1 of 636# 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 pass speaker1 ⟨Dog A⟩ 2 ⟨Robot C⟩ 3 ⟨Dog A⟩ 4 ⟨Robot C⟩ 5 ⟨Dog A⟩ 6 ⟨Robot C⟩ def speak(self):
pass 1 of 77def speak(self⟨Dog A⟩):8 return "Woof!"print(f" {make_speak(s)}")
109for s in speakers:110 print(f" {make_speak(s⟨Dog A⟩)}")output Woof!for s in speakers:
pass 2 of 2108print("--- make_speak (basic polymorphism) ---")109for s⟨Robot C⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:110 print(f" {make_speak(s⟨Robot C⟩)}")def speak(self):
pass 1 of 729def speak(self⟨Robot C⟩):30 return "Beep boop!"print(f" {make_speak(s)}")
109for s in speakers:110 print(f" {make_speak(s⟨Robot C⟩)}")output Beep boop!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) ---for s in speakers:
pass 1 of 2113print("\n--- describe_speaker (with type handling) ---")114for s⟨Dog A⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:115 print(f" {describe_speaker(s⟨Dog A⟩)}")def describe_speaker(speaker):
pass 1 of 245# 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()sound ← Woof!
49"""50sound→ Woof! = speaker⟨Dog A⟩.speak()identifier ← Buddy
52# Can use hasattr to check for attributes53if hasattr(speaker⟨Dog A⟩, 'name'):54 identifier→ Buddy = speaker.nameBuddy55elif hasattr(speaker, 'model'):return f"{identifier} says: {sound}"
60return f"{identifierBuddy} says: {soundWoof!}"print(f" {describe_speaker(s)}")
114for s in speakers:115 print(f" {describe_speaker(s⟨Dog A⟩)}")output Buddy says: Woof!for s in speakers:
pass 2 of 2113print("\n--- describe_speaker (with type handling) ---")114for s⟨Robot C⟩ in speakers[⟨Dog A⟩, ⟨Robot C⟩]:115 print(f" {describe_speaker(s⟨Robot C⟩)}")def describe_speaker(speaker):
pass 2 of 245# 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()sound ← Beep boop!
49"""50sound→ Beep boop! = speaker⟨Robot C⟩.speak()identifier ← R2D2
54 identifier = speaker.name55elif hasattr(speaker⟨Robot C⟩, 'model'):56 identifier→ R2D2 = speaker.modelR2D257else:return f"{identifier} says: {sound}"
60return f"{identifierR2D2} says: {soundBeep boop!}"print(f" {describe_speaker(s)}")
114for s in speakers:115 print(f" {describe_speaker(s⟨Robot C⟩)}")output R2D2 says: Beep boop!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) ---for s in speakers:
pass 1 of 2118print("\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⟩)}")def show_length(obj):
pass 1 of 563# 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 pass objselfself.nameself.model1 ⟨Dog A⟩ ⟨Dog A⟩ Buddy — 2 ⟨Robot C⟩ ⟨Robot C⟩ — R2D2 3 Hello — — — 4 [1, 2, 3] — — — 5 {'a': 1, 'b': 2} — — — def __len__(self):
10def __len__(self⟨Dog A⟩):11 return len(self.nameBuddy)print(f" {show_length(s)}")
119for s in speakers:120 print(f" {show_length(s⟨Dog A⟩)}")output Length of Dog: 5for s in speakers:
pass 2 of 2118print("\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⟩)}")def __len__(self):
32def __len__(self⟨Robot C⟩):33 return len(self.modelR2D2)print(f" {show_length(s)}")
119for s in speakers:120 print(f" {show_length(s⟨Robot C⟩)}")output Length of Robot: 4print(" 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: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: 5print(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: 3make_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: 2def 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! **for _ in range(times):
pass 1 of 278print("\n** Chorus Time! **")79for _0 in range(times2):80 for speaker in speakers:81 print(f" {make_speak(speaker)}")for speaker in speakers:
pass 1 of 479for _ 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 pass speaker1 ⟨Dog A⟩ 2 ⟨Robot C⟩ 3 ⟨Dog A⟩ 4 ⟨Robot C⟩ print(f" {make_speak(speaker)}")
80for speaker in speakers:81 print(f" {make_speak(speaker⟨Dog A⟩)}")82print("---")output Woof!print(f" {make_speak(speaker)}")
80for speaker in speakers:81 print(f" {make_speak(speaker⟨Robot C⟩)}")82print("---")output Beep boop!print("---")
81 print(f" {make_speak(speaker)}")82print("---")output---for _ in range(times):
pass 2 of 278print("\n** Chorus Time! **")79for _1 in range(times2):80 for speaker in speakers:81 print(f" {make_speak(speaker)}")print(f" {make_speak(speaker)}")
80for speaker in speakers:81 print(f" {make_speak(speaker⟨Dog A⟩)}")82print("---")output Woof!print(f" {make_speak(speaker)}")
80for speaker in speakers:81 print(f" {make_speak(speaker⟨Robot C⟩)}")82print("---")output Beep boop!print("---")
81 print(f" {make_speak(speaker)}")82print("---")output---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:results ← []
pass 1 of 285# 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:for speaker in speakers:
pass 1 of 491results = []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 pass speakers1 ⟨Dog A⟩ ⟨Dog A⟩ 2 ⟨Robot C⟩ ⟨Robot C⟩ 3 ⟨Dog A⟩ ⟨Dog A⟩ 4 ⟨Robot C⟩ ⟨Robot C⟩ def loud_speak(s):
pass 1 of 2134# Different processors135def loud_speak(s⟨Dog A⟩):136 return s⟨Dog A⟩.speak().upper() + "!!"result ← WOOF!!!, results ← ['WOOF!!!']
92for speaker in speakers:93 result→ WOOF!!! = processor(speaker⟨Dog A⟩)94 results→ ['WOOF!!!'].append(resultWOOF!!!)95return resultsdef loud_speak(s):
pass 2 of 2134# Different processors135def loud_speak(s⟨Robot C⟩):136 return s⟨Robot C⟩.speak().upper() + "!!"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!!!']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:for r in results:
pass 1 of 2142results = process_speakers(speakers, loud_speak)143for rWOOF!!! in results['WOOF!!!', 'BEEP BOOP!!!']:144 print(f" {rWOOF!!!}")output WOOF!!!for r in results:
pass 2 of 2142results = process_speakers(speakers, loud_speak)143for rBEEP BOOP!!! in results['WOOF!!!', 'BEEP BOOP!!!']:144 print(f" {rBEEP BOOP!!!}")output BEEP BOOP!!!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:results ← []
pass 2 of 285# 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:def count_speak(s):
pass 1 of 2138def count_speak(s⟨Dog A⟩):139 return f"{s⟨Dog A⟩.speak()} ({len(s.speak())} chars)"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 resultsdef count_speak(s):
pass 2 of 2138def count_speak(s⟨Robot C⟩):139 return f"{s⟨Robot C⟩.speak()} ({len(s.speak())} chars)"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)']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:for r in results:
pass 1 of 2147results = 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)for r in results:
pass 2 of 2147results = 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)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
print("=== Polymorphic Functions === ")
98print("=== Polymorphic Functions ===\n")99100# Create different objects101dog = Dog("Buddy")102cat = Cat("Whiskers")output=== Polymorphic Functions ===self.name ← Buddy
3class Dog:4 def __init__(self⟨Dog A⟩, nameBuddy):5 self.name→ Buddy = nameBuddydog ← ⟨Dog A⟩
100# Create different objects101dog→ ⟨Dog A⟩ = Dog("Buddy")102cat = Cat("Whiskers")103robot = Robot("R2D2")self.name ← Whiskers
14class Cat:15 def __init__(self⟨Cat B⟩, nameWhiskers):16 self.name→ Whiskers = nameWhiskerscat ← ⟨Cat B⟩
101dog = Dog("Buddy")102cat→ ⟨Cat B⟩ = Cat("Whiskers")103robot = Robot("R2D2")self.model ← R2D2
25class Robot:26 def __init__(self⟨Robot C⟩, modelR2D2):27 self.model→ R2D2 = modelR2D2robot ← ⟨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) ---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⟩)}")def make_speak(speaker):
pass 1 of 336# 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()def speak(self):
pass 1 of 718def speak(self⟨Cat B⟩):19 return "Meow!"print(f" {make_speak(s)}")
109for s in speakers:110 print(f" {make_speak(s⟨Cat B⟩)}")output Meow!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) ---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⟩)}")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()sound ← Meow!
49"""50sound→ Meow! = speaker⟨Cat B⟩.speak()identifier ← Whiskers
52# Can use hasattr to check for attributes53if hasattr(speaker⟨Cat B⟩, 'name'):54 identifier→ Whiskers = speaker.nameWhiskers55elif hasattr(speaker, 'model'):return f"{identifier} says: {sound}"
60return f"{identifierWhiskers} says: {soundMeow!}"print(f" {describe_speaker(s)}")
114for s in speakers:115 print(f" {describe_speaker(s⟨Cat B⟩)}")output Whiskers says: Meow!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) ---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⟩)}")def show_length(obj):
pass 1 of 463# 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 pass objselfself.name1 ⟨Cat B⟩ ⟨Cat B⟩ Whiskers 2 Hello — — 3 [1, 2, 3] — — 4 {'a': 1, 'b': 2} — — def __len__(self):
21def __len__(self⟨Cat B⟩):22 return len(self.nameWhiskers)print(f" {show_length(s)}")
119for s in speakers:120 print(f" {show_length(s⟨Cat B⟩)}")output Length of Cat: 8print(" 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: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: 5print(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: 3make_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: 2def 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! **for _ in range(times):
pass 1 of 278print("\n** Chorus Time! **")79for _0 in range(times2):80 for speaker in speakers:81 print(f" {make_speak(speaker)}")for speaker in speakers:
pass 1 of 279for _ in range(times):80 for speaker⟨Cat B⟩ in speakers[⟨Cat B⟩]:81 print(f" {make_speak(speaker⟨Cat B⟩)}")82 print("---")print(f" {make_speak(speaker)}")
80for speaker in speakers:81 print(f" {make_speak(speaker⟨Cat B⟩)}")82print("---")output Meow!print("---")
81 print(f" {make_speak(speaker)}")82print("---")output---for _ in range(times):
pass 2 of 278print("\n** Chorus Time! **")79for _1 in range(times2):80 for speaker in speakers:81 print(f" {make_speak(speaker)}")for speaker in speakers:
pass 2 of 279for _ in range(times):80 for speaker⟨Cat B⟩ in speakers[⟨Cat B⟩]:81 print(f" {make_speak(speaker⟨Cat B⟩)}")82 print("---")print(f" {make_speak(speaker)}")
80for speaker in speakers:81 print(f" {make_speak(speaker⟨Cat B⟩)}")82print("---")output Meow!print("---")
81 print(f" {make_speak(speaker)}")82print("---")output---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:results ← []
pass 1 of 285# 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:for speaker in speakers:
pass 1 of 291results = []92for speaker⟨Cat B⟩ in speakers[⟨Cat B⟩]:93 result = processor(speaker⟨Cat B⟩)94 results.append(result)def loud_speak(s):
134# Different processors135def loud_speak(s⟨Cat B⟩):136 return s⟨Cat B⟩.speak().upper() + "!!"result ← MEOW!!!, results ← ['MEOW!!!']
92for speaker in speakers:93 result→ MEOW!!! = processor(speaker⟨Cat B⟩)94 results→ ['MEOW!!!'].append(resultMEOW!!!)95return results['MEOW!!!']results ← ['MEOW!!!']
141print("Loud processor:")142results→ ['MEOW!!!'] = process_speakers(speakers[⟨Cat B⟩], loud_speak⟨function loud_speak D⟩)143for r in results:for r in results:
142results = process_speakers(speakers, loud_speak)143for rMEOW!!! in results['MEOW!!!']:144 print(f" {rMEOW!!!}")output MEOW!!!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:results ← []
pass 2 of 285# 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:for speaker in speakers:
pass 2 of 291results = []92for speaker⟨Cat B⟩ in speakers[⟨Cat B⟩]:93 result = processor(speaker⟨Cat B⟩)94 results.append(result)def count_speak(s):
138def count_speak(s⟨Cat B⟩):139 return f"{s⟨Cat B⟩.speak()} ({len(s.speak())} chars)"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)']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: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)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 +, -, ==.
# 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
""")
"""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) #?createv2output=== Operator Overloading === --- Vector Operations ---self.x ← 3, self.y ← 4
pass 1 of 76def __init__(self(empty), x3, y4): #?vectorinit7 self.x→ 3 = x38 self.y→ 4 = y4All 7 passes — pass 1 is the card above pass xyotherother.xother.yself.xself.y1 3 4 — — — 3 4 2 1 2 — — — 1 2 3 4 6 — — — 4 6 4 2 2 — — — 2 2 5 6 8 — — — 6 8 6 3 6 — — — 3 6 7 3 4 Vector(3, 4) 3 4 3 4 v1 ← Vector(3, 4)
118print("--- Vector Operations ---")119v1→ Vector(3, 4) = Vector(3, 4) #?createv1120v2 = Vector(1, 2) #?createv2v2 ← 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}") #?subvectorsoutputv1 = Vector(3, 4) v2 = Vector(1, 2)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) #?addreturnprint(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}") #?mulscalaroutputv1 + v2 = Vector(4, 6)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)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}") #?rmulscalaroutputv1 - v2 = Vector(2, 2)def __mul__(self, scalar): #?vectormul
pass 1 of 221def __mul__(selfVector(3, 4), scalar2): #?vectormul22 """v * scalar"""23 return Vector(self.x3 * scalar2, self.y4 * scalar) #?mulreturnprint(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}") #?absvectoroutputv1 * 2 = Vector(6, 8)def __rmul__(self, scalar): #?vectorrmul
25def __rmul__(selfVector(1, 2), scalar3): #?vectorrmul26 """scalar * v (reversed)"""27 return self.__mul__(scalar3) #?rmuldelegatedef __mul__(self, scalar): #?vectormul
pass 2 of 221def __mul__(selfVector(1, 2), scalar3): #?vectormul22 """v * scalar"""23 return Vector(self.x1 * scalar3, self.y2 * scalar) #?mulreturnprint(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}") #?eqvectorsoutput3 * v2 = Vector(3, 6)def __abs__(self): #?vectorabs
33def __abs__(selfVector(3, 4)): #?vectorabs34 """abs(v) - magnitude"""35 return (self.x3 ** 2 + self.y4 ** 2) ** 0.5 #?magnitudeprint(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)}") #?eqsameoutput|v1| = 5.00def __eq__(self, other): #?vectoreq
pass 1 of 229def __eq__(selfVector(3, 4), otherVector(1, 2)): #?vectoreq30 """v1 == v2"""31 return self.x3 == other.x1 and self.y4 == other.y2 #?eqreturnprint(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)}") #?eqsameoutputv1 == v2: Falsedef __eq__(self, other): #?vectoreq
pass 2 of 229def __eq__(selfVector(3, 4), otherVector(3, 4)): #?vectoreq30 """v1 == v2"""31 return self.x3 == other.x3 and self.y4 == other.y4 #?eqreturnprint(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) #?createtaxoutputv1 == Vector(3, 4): True --- Money Operations ---total_cents ← 1999, self.dollars ← 19, self.cents ← 99
pass 1 of 741def __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 % 100All 7 passes — pass 1 is the card above pass dollarscentsotherother.dollarsother.centstotal_centsself.dollarsself.cents1 19 99 — — — 1999 19 99 2 1 60 — — — 160 1 60 3 5 0 — — — 500 5 0 4 21 59 — — — 2159 21 59 5 16 59 — — — 1659 16 59 6 39 98 — — — 3998 39 98 7 19 99 $19.99 19 99 1999 19 99 price ← $19.99
134print("\n--- Money Operations ---")135price→ $19.99 = Money(19, 99) #?createprice136tax = Money(1, 60) #?createtax137discount = Money(5, 0) #?creatediscounttax ← $1.60
135price = Money(19, 99) #?createprice136tax→ $1.60 = Money(1, 60) #?createtax137discount = Money(5, 0) #?creatediscountdiscount ← $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.00total_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)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.59total_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)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.59total_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)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)}") #?eqmoneyoutputPrice * 2 = $39.98def __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)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)}") #?eqmoneyoutputTax < Discount: Truedef __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.cents99print(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") #?createveggiesoutputPrice == Money(19, 99): True --- StringList Operations ---self.items ← ['apple', 'banana']
pass 1 of 388def __init__(self(empty), *items('apple', 'banana')): #?stringlistinit89 self.items→ ['apple', 'banana'] = list(items('apple', 'banana'))All 3 passes — pass 1 is the card above pass itemsself.items1 ('apple', 'banana') ['apple', 'banana'] 2 ('carrot', 'broccoli') ['carrot', 'broccoli'] 3 ('apple', 'banana', 'carrot', 'broccoli') ['apple', 'banana', 'carrot', 'broccoli'] fruits ← StringList('apple', 'banana')
156print("\n--- StringList Operations ---")157fruits→ StringList('apple', 'banana') = StringList("apple", "banana") #?createfruits158veggies = StringList("carrot", "broccoli") #?createveggiesveggies ← 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')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'])) #?concatreturncombined ← 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]}") #?indexcombinedoutputcombined = StringList('apple', 'banana', 'carrot', 'broccoli')def __len__(self): #?stringlistlen
98def __len__(selfStringList('apple', 'banana', 'carrot', 'broccoli')): #?stringlistlen99 """len(sl)"""100 return len(self.items['apple', 'banana', 'carrot', 'broccoli'])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}") #?incombinedoutputlen(combined) = 4def __getitem__(self, index): #?stringlistget
102def __getitem__(selfStringList('apple', 'banana', 'carrot', 'broccoli'), index0): #?stringlistget103 """sl[index]"""104 return self.items[index]apple #?getreturnprint(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')}") #?incombinedoutputcombined[0] = appledef __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'] #?containsreturnprint(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: #?iteratecombinedoutput'apple' in combined: True Iterating: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']) #?iterreturnfor item in combined: #?iteratecombined
pass 1 of 4170print("Iterating:")171for itemapple in combinedStringList('apple', 'banana', 'carrot', 'broccoli'): #?iteratecombined172 print(f" - {itemapple}")output - appleAll 4 passes — pass 1 is the card above pass item1 apple 2 banana 3 carrot 4 broccoli 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.
Exercise: practical.py
Build a payment system with polymorphic processors