Object-Oriented Basics
self
The Current Object
When you call dog.bark(), how does bark() know which dog? Python passes the
object automatically as the first parameter, called self. It's how methods
access the object's data and other methods.
What self is
The current object instance.
# Understanding self as Current Object
print("=== What is self? ===\n")
class Dog:
def __init__(self, name):
print(f"__init__ called, self is: {self}")
self.name = name
def bark(self):
print(f"bark() called, self is: {self}")
print(f"{self.name} says: Woof!")
# Create two dogs
buddy = Dog("Buddy")
print()
max_dog = Dog("Max")
print(f"\nbuddy is: {buddy}")
print(f"max_dog is: {max_dog}")
# self becomes the object before the dot
print("\n=== self = Object Before the Dot ===")
buddy.bark() # self = buddy
print()
max_dog.bark() # self = max_dog
print("\n=== Manual Call (Don't Do This) ===")
# These are equivalent:
buddy.bark() # Normal way
Dog.bark(buddy) # What Python actually does
print("\n=== Each Object Has Its Own self ===")
class Counter:
def __init__(self, start=0):
self.count = start
def increment(self):
self.count += 1
def display(self):
print(f"This counter ({id(self)}): {self.count}")
c1 = Counter(0)
c2 = Counter(100)
# Each operates on its own count
c1.increment()
c1.increment()
c2.increment()
c1.display() # self = c1
c2.display() # self = c2
print("\n=== self is Just a Name ===")
class Example:
def method1(self):
print(f"self = {self}")
def method2(this):
print(f"this = {this}") # Works! But don't do it
def method3(potato):
print(f"potato = {potato}") # Also works! Very bad style
obj = Example()
obj.method1() # self
obj.method2() # this (same object)
obj.method3() # potato (same object)
print("\nAlways use 'self' - it's Python convention!")
# Understanding self as Current Object
print("=== What is self? ===\n")
class Dog:
def __init__(self, name):
print(f"__init__ called, self is: {self}")
self.name = name
def bark(self):
print(f"bark() called, self is: {self}")
print(f"{self.name} says: Woof!")
# Create two dogs
buddy = Dog("Rex")
print()
max_dog = Dog("Max")
print(f"\nbuddy is: {buddy}")
print(f"max_dog is: {max_dog}")
# self becomes the object before the dot
print("\n=== self = Object Before the Dot ===")
buddy.bark() # self = buddy
print()
max_dog.bark() # self = max_dog
print("\n=== Manual Call (Don't Do This) ===")
# These are equivalent:
buddy.bark() # Normal way
Dog.bark(buddy) # What Python actually does
print("\n=== Each Object Has Its Own self ===")
class Counter:
def __init__(self, start=0):
self.count = start
def increment(self):
self.count += 1
def display(self):
print(f"This counter ({id(self)}): {self.count}")
c1 = Counter(0)
c2 = Counter(100)
# Each operates on its own count
c1.increment()
c1.increment()
c2.increment()
c1.display() # self = c1
c2.display() # self = c2
print("\n=== self is Just a Name ===")
class Example:
def method1(self):
print(f"self = {self}")
def method2(this):
print(f"this = {this}") # Works! But don't do it
def method3(potato):
print(f"potato = {potato}") # Also works! Very bad style
obj = Example()
obj.method1() # self
obj.method2() # this (same object)
obj.method3() # potato (same object)
print("\nAlways use 'self' - it's Python convention!")
# Understanding self as Current Object
print("=== What is self? ===\n")
class Dog:
def __init__(self, name):
print(f"__init__ called, self is: {self}")
self.name = name
def bark(self):
print(f"bark() called, self is: {self}")
print(f"{self.name} says: Woof!")
# Create two dogs
buddy = Dog("Buddy")
print()
max_dog = Dog("Max")
print(f"\nbuddy is: {buddy}")
print(f"max_dog is: {max_dog}")
# self becomes the object before the dot
print("\n=== self = Object Before the Dot ===")
buddy.bark() # self = buddy
print()
max_dog.bark() # self = max_dog
print("\n=== Manual Call (Don't Do This) ===")
# These are equivalent:
buddy.bark() # Normal way
Dog.bark(buddy) # What Python actually does
print("\n=== Each Object Has Its Own self ===")
class Counter:
def __init__(self, start=0):
self.count = start
def increment(self):
self.count += 1
def display(self):
print(f"This counter ({id(self)}): {self.count}")
c1 = Counter(0)
c2 = Counter(10)
# Each operates on its own count
c1.increment()
c1.increment()
c2.increment()
c1.display() # self = c1
c2.display() # self = c2
print("\n=== self is Just a Name ===")
class Example:
def method1(self):
print(f"self = {self}")
def method2(this):
print(f"this = {this}") # Works! But don't do it
def method3(potato):
print(f"potato = {potato}") # Also works! Very bad style
obj = Example()
obj.method1() # self
obj.method2() # this (same object)
obj.method3() # potato (same object)
print("\nAlways use 'self' - it's Python convention!")
# Understanding self as Current Object
print("=== What is self? ===\n")
class Dog:
def __init__(self, name):
print(f"__init__ called, self is: {self}")
self.name = name
def bark(self):
print(f"bark() called, self is: {self}")
print(f"{self.name} says: Woof!")
# Create two dogs
buddy = Dog("Buddy")
print()
max_dog = Dog("Max")
print(f"\nbuddy is: {buddy}")
print(f"max_dog is: {max_dog}")
# self becomes the object before the dot
print("\n=== self = Object Before the Dot ===")
buddy.bark() # self = buddy
print()
max_dog.bark() # self = max_dog
print("\n=== Manual Call (Don't Do This) ===")
# These are equivalent:
buddy.bark() # Normal way
Dog.bark(buddy) # What Python actually does
print("\n=== Each Object Has Its Own self ===")
class Counter:
def __init__(self, start=0):
self.count = start
def increment(self):
self.count += 1
def display(self):
print(f"This counter ({id(self)}): {self.count}")
c1 = Counter(0)
c2 = Counter(500)
# Each operates on its own count
c1.increment()
c1.increment()
c2.increment()
c1.display() # self = c1
c2.display() # self = c2
print("\n=== self is Just a Name ===")
class Example:
def method1(self):
print(f"self = {self}")
def method2(this):
print(f"this = {this}") # Works! But don't do it
def method3(potato):
print(f"potato = {potato}") # Also works! Very bad style
obj = Example()
obj.method1() # self
obj.method2() # this (same object)
obj.method3() # potato (same object)
print("\nAlways use 'self' - it's Python convention!")
print("=== What is self? === ")
3print("=== What is self? ===\n")45class Dog:6 def __init__(self, name): #?initself7 print(f"__init__ called, self is: {self}")8 self.name = name #?selfname9 10 def bark(self): #?barkself11 print(f"bark() called, self is: {self}")12 print(f"{self.name} says: Woof!")1314# Create two dogs #?twodogs15buddy = Dog("Buddy") #@buddy=Dog("Rex")16print()output=== What is self? ===self.name ← Buddy
pass 1 of 25class Dog:6 def __init__(self⟨Dog A⟩, nameBuddy): #?initself7 print(f"__init__ called, self is: {self}")8 self.name→ Buddy = nameBuddy #?selfnameoutput__init__ called, self is: ⟨Dog A⟩buddy ← ⟨Dog A⟩
14# Create two dogs #?twodogs15buddy→ ⟨Dog A⟩ = Dog("Buddy") #@buddy=Dog("Rex")16print()17max_dog = Dog("Max")self.name ← Max
pass 2 of 25class Dog:6 def __init__(self⟨Dog B⟩, nameMax): #?initself7 print(f"__init__ called, self is: {self}")8 self.name→ Max = nameMax #?selfnameoutput__init__ called, self is: ⟨Dog B⟩max_dog ← ⟨Dog B⟩
16print()17max_dog→ ⟨Dog B⟩ = Dog("Max")1819print(f"\nbuddy is: {buddy⟨Dog A⟩}")20print(f"max_dog is: {max_dog⟨Dog B⟩}")2122# self becomes the object before the dot #?beforedot23print("\n=== self = Object Before the Dot ===")24buddy⟨Dog A⟩.bark() # self = buddy #?selfisbuddy25print()output buddy is: ⟨Dog A⟩ max_dog is: ⟨Dog B⟩ === self = Object Before the Dot ===def bark(self): #?barkself
pass 1 of 410def bark(self⟨Dog A⟩): #?barkself11 print(f"bark() called, self is: {self}")12 print(f"{self.nameBuddy} says: Woof!")outputbark() called, self is: ⟨Dog A⟩ Buddy says: Woof!All 4 passes — pass 1 is the card above pass selfself.name1 ⟨Dog A⟩ Buddy 2 ⟨Dog B⟩ Max 3 ⟨Dog A⟩ Buddy 4 ⟨Dog A⟩ Buddy buddy.bark() # self = buddy #?selfisbuddy
23print("\n=== self = Object Before the Dot ===")24buddy⟨Dog A⟩.bark() # self = buddy #?selfisbuddy25print()26max_dog⟨Dog B⟩.bark() # self = max_dog #?selfismaxmax_dog.bark() # self = max_dog #?selfismax
25print()26max_dog⟨Dog B⟩.bark() # self = max_dog #?selfismax2728print("\n=== Manual Call (Don't Do This) ===")2930# These are equivalent: #?equivalent31buddy⟨Dog A⟩.bark() # Normal way32Dog.bark(buddy) # What Python actually does #?explicitselfoutput === Manual Call (Don't Do This) ===buddy.bark() # Normal way
30# These are equivalent: #?equivalent31buddy⟨Dog A⟩.bark() # Normal way32Dog<class '__main__.Dog'>.bark(buddy⟨Dog A⟩) # What Python actually does #?explicitselfDog.bark(buddy) # What Python actually does #?explicitself
31buddy.bark() # Normal way32Dog<class '__main__.Dog'>.bark(buddy⟨Dog A⟩) # What Python actually does #?explicitself3334print("\n=== Each Object Has Its Own self ===")3536class Counter:37 def __init__(self, start=0):38 self.count = start #?owncount39 40 def increment(self):41 self.count += 1 #?incrementself42 43 def display(self):44 print(f"This counter ({id(self)}): {self.count}") #?idself4546c1 = Counter(0)47c2 = Counter(100) #@c2=Counter(10), Counter(500)output === Each Object Has Its Own self ===self.count ← 0
pass 1 of 236class Counter:37 def __init__(self⟨Counter C⟩, start0=0):38 self.count→ 0 = start0 #?owncountc1 ← ⟨Counter C⟩
46c1→ ⟨Counter C⟩ = Counter(0)47c2 = Counter(100) #@c2=Counter(10), Counter(500)self.count ← 100
pass 2 of 236class Counter:37 def __init__(self⟨Counter D⟩, start100=0):38 self.count→ 100 = start100 #?owncountc2 ← ⟨Counter D⟩
46c1 = Counter(0)47c2→ ⟨Counter D⟩ = Counter(100) #@c2=Counter(10), Counter(500)4849# Each operates on its own count #?eachown50c1⟨Counter C⟩.increment()51c1.increment()self.count ← 1
pass 1 of 340def increment(self⟨Counter C⟩):41 self.count→ 1 += 1 #?incrementselfAll 3 passes — pass 1 is the card above pass selfself.count1 ⟨Counter C⟩ 0 → 1 2 ⟨Counter C⟩ 1 → 2 3 ⟨Counter D⟩ 100 → 101 c1.increment()
49# Each operates on its own count #?eachown50c1⟨Counter C⟩.increment()51c1⟨Counter C⟩.increment()52c2.increment()c1.increment()
50c1.increment()51c1⟨Counter C⟩.increment()52c2⟨Counter D⟩.increment()c2.increment()
51c1.increment()52c2⟨Counter D⟩.increment()5354c1⟨Counter C⟩.display() # self = c155c2.display() # self = c2def display(self):
pass 1 of 243def display(self⟨Counter C⟩):44 print(f"This counter ({id(self)}): {self.count2}") #?idselfoutputThis counter (124973150205280): 2c1.display() # self = c1
54c1⟨Counter C⟩.display() # self = c155c2⟨Counter D⟩.display() # self = c2def display(self):
pass 2 of 243def display(self⟨Counter D⟩):44 print(f"This counter ({id(self)}): {self.count101}") #?idselfoutputThis counter (124973150135472): 101obj ← ⟨Example E⟩
54c1.display() # self = c155c2⟨Counter D⟩.display() # self = c25657print("\n=== self is Just a Name ===")5859class Example:60 def method1(self): #?conventional61 print(f"self = {self}")62 63 def method2(this): #?thisworks64 print(f"this = {this}") # Works! But don't do it65 66 def method3(potato): #?potatoworks67 print(f"potato = {potato}") # Also works! Very bad style6869obj→ ⟨Example E⟩ = Example()70obj⟨Example E⟩.method1() # self71obj.method2() # this (same object)output === self is Just a Name ===def method1(self): #?conventional
59class Example:60 def method1(self⟨Example E⟩): #?conventional61 print(f"self = {self}")outputself = ⟨Example E⟩obj.method1() # self
69obj = Example()70obj⟨Example E⟩.method1() # self71obj⟨Example E⟩.method2() # this (same object)72obj.method3() # potato (same object)def method2(this): #?thisworks
63def method2(this⟨Example E⟩): #?thisworks64 print(f"this = {this⟨Example E⟩}") # Works! But don't do itoutputthis = ⟨Example E⟩obj.method2() # this (same object)
70obj.method1() # self71obj⟨Example E⟩.method2() # this (same object)72obj⟨Example E⟩.method3() # potato (same object)def method3(potato): #?potatoworks
66def method3(potato⟨Example E⟩): #?potatoworks67 print(f"potato = {potato⟨Example E⟩}") # Also works! Very bad styleoutputpotato = ⟨Example E⟩obj.method3() # potato (same object)
71obj.method2() # this (same object)72obj⟨Example E⟩.method3() # potato (same object)7374print("\nAlways use 'self' - it's Python convention!")75#@help initselfoutput Always use 'self' - it's Python convention!
print("=== What is self? === ")
3print("=== What is self? ===\n")45class Dog:6 def __init__(self, name):7 print(f"__init__ called, self is: {self}")8 self.name = name9 10 def bark(self):11 print(f"bark() called, self is: {self}")12 print(f"{self.name} says: Woof!")1314# Create two dogs15buddy = Dog("Rex")16print()output=== What is self? ===self.name ← Rex
pass 1 of 25class Dog:6 def __init__(self⟨Dog A⟩, nameRex):7 print(f"__init__ called, self is: {self}")8 self.name→ Rex = nameRexoutput__init__ called, self is: ⟨Dog A⟩buddy ← ⟨Dog A⟩
14# Create two dogs15buddy→ ⟨Dog A⟩ = Dog("Rex")16print()17max_dog = Dog("Max")self.name ← Max
pass 2 of 25class Dog:6 def __init__(self⟨Dog B⟩, nameMax):7 print(f"__init__ called, self is: {self}")8 self.name→ Max = nameMaxoutput__init__ called, self is: ⟨Dog B⟩max_dog ← ⟨Dog B⟩
16print()17max_dog→ ⟨Dog B⟩ = Dog("Max")1819print(f"\nbuddy is: {buddy⟨Dog A⟩}")20print(f"max_dog is: {max_dog⟨Dog B⟩}")2122# self becomes the object before the dot23print("\n=== self = Object Before the Dot ===")24buddy⟨Dog A⟩.bark() # self = buddy25print()output buddy is: ⟨Dog A⟩ max_dog is: ⟨Dog B⟩ === self = Object Before the Dot ===def bark(self):
pass 1 of 410def bark(self⟨Dog A⟩):11 print(f"bark() called, self is: {self}")12 print(f"{self.nameRex} says: Woof!")outputbark() called, self is: ⟨Dog A⟩ Rex says: Woof!All 4 passes — pass 1 is the card above pass selfself.name1 ⟨Dog A⟩ Rex 2 ⟨Dog B⟩ Max 3 ⟨Dog A⟩ Rex 4 ⟨Dog A⟩ Rex buddy.bark() # self = buddy
23print("\n=== self = Object Before the Dot ===")24buddy⟨Dog A⟩.bark() # self = buddy25print()26max_dog⟨Dog B⟩.bark() # self = max_dogmax_dog.bark() # self = max_dog
25print()26max_dog⟨Dog B⟩.bark() # self = max_dog2728print("\n=== Manual Call (Don't Do This) ===")2930# These are equivalent:31buddy⟨Dog A⟩.bark() # Normal way32Dog.bark(buddy) # What Python actually doesoutput === Manual Call (Don't Do This) ===buddy.bark() # Normal way
30# These are equivalent:31buddy⟨Dog A⟩.bark() # Normal way32Dog<class '__main__.Dog'>.bark(buddy⟨Dog A⟩) # What Python actually doesDog.bark(buddy) # What Python actually does
31buddy.bark() # Normal way32Dog<class '__main__.Dog'>.bark(buddy⟨Dog A⟩) # What Python actually does3334print("\n=== Each Object Has Its Own self ===")3536class Counter:37 def __init__(self, start=0):38 self.count = start39 40 def increment(self):41 self.count += 142 43 def display(self):44 print(f"This counter ({id(self)}): {self.count}")4546c1 = Counter(0)47c2 = Counter(100)output === Each Object Has Its Own self ===self.count ← 0
pass 1 of 236class Counter:37 def __init__(self⟨Counter C⟩, start0=0):38 self.count→ 0 = start0c1 ← ⟨Counter C⟩
46c1→ ⟨Counter C⟩ = Counter(0)47c2 = Counter(100)self.count ← 100
pass 2 of 236class Counter:37 def __init__(self⟨Counter D⟩, start100=0):38 self.count→ 100 = start100c2 ← ⟨Counter D⟩
46c1 = Counter(0)47c2→ ⟨Counter D⟩ = Counter(100)4849# Each operates on its own count50c1⟨Counter C⟩.increment()51c1.increment()self.count ← 1
pass 1 of 340def increment(self⟨Counter C⟩):41 self.count→ 1 += 1All 3 passes — pass 1 is the card above pass selfself.count1 ⟨Counter C⟩ 0 → 1 2 ⟨Counter C⟩ 1 → 2 3 ⟨Counter D⟩ 100 → 101 c1.increment()
49# Each operates on its own count50c1⟨Counter C⟩.increment()51c1⟨Counter C⟩.increment()52c2.increment()c1.increment()
50c1.increment()51c1⟨Counter C⟩.increment()52c2⟨Counter D⟩.increment()c2.increment()
51c1.increment()52c2⟨Counter D⟩.increment()5354c1⟨Counter C⟩.display() # self = c155c2.display() # self = c2def display(self):
pass 1 of 243def display(self⟨Counter C⟩):44 print(f"This counter ({id(self)}): {self.count2}")outputThis counter (123362345579632): 2c1.display() # self = c1
54c1⟨Counter C⟩.display() # self = c155c2⟨Counter D⟩.display() # self = c2def display(self):
pass 2 of 243def display(self⟨Counter D⟩):44 print(f"This counter ({id(self)}): {self.count101}")outputThis counter (123362345509632): 101obj ← ⟨Example E⟩
54c1.display() # self = c155c2⟨Counter D⟩.display() # self = c25657print("\n=== self is Just a Name ===")5859class Example:60 def method1(self):61 print(f"self = {self}")62 63 def method2(this):64 print(f"this = {this}") # Works! But don't do it65 66 def method3(potato):67 print(f"potato = {potato}") # Also works! Very bad style6869obj→ ⟨Example E⟩ = Example()70obj⟨Example E⟩.method1() # self71obj.method2() # this (same object)output === self is Just a Name ===def method1(self):
59class Example:60 def method1(self⟨Example E⟩):61 print(f"self = {self}")outputself = ⟨Example E⟩obj.method1() # self
69obj = Example()70obj⟨Example E⟩.method1() # self71obj⟨Example E⟩.method2() # this (same object)72obj.method3() # potato (same object)def method2(this):
63def method2(this⟨Example E⟩):64 print(f"this = {this⟨Example E⟩}") # Works! But don't do itoutputthis = ⟨Example E⟩obj.method2() # this (same object)
70obj.method1() # self71obj⟨Example E⟩.method2() # this (same object)72obj⟨Example E⟩.method3() # potato (same object)def method3(potato):
66def method3(potato⟨Example E⟩):67 print(f"potato = {potato⟨Example E⟩}") # Also works! Very bad styleoutputpotato = ⟨Example E⟩obj.method3() # potato (same object)
71obj.method2() # this (same object)72obj⟨Example E⟩.method3() # potato (same object)7374print("\nAlways use 'self' - it's Python convention!")output Always use 'self' - it's Python convention!
print("=== What is self? === ")
3print("=== What is self? ===\n")45class Dog:6 def __init__(self, name):7 print(f"__init__ called, self is: {self}")8 self.name = name9 10 def bark(self):11 print(f"bark() called, self is: {self}")12 print(f"{self.name} says: Woof!")1314# Create two dogs15buddy = Dog("Buddy")16print()output=== What is self? ===self.name ← Buddy
pass 1 of 25class Dog:6 def __init__(self⟨Dog A⟩, nameBuddy):7 print(f"__init__ called, self is: {self}")8 self.name→ Buddy = nameBuddyoutput__init__ called, self is: ⟨Dog A⟩buddy ← ⟨Dog A⟩
14# Create two dogs15buddy→ ⟨Dog A⟩ = Dog("Buddy")16print()17max_dog = Dog("Max")self.name ← Max
pass 2 of 25class Dog:6 def __init__(self⟨Dog B⟩, nameMax):7 print(f"__init__ called, self is: {self}")8 self.name→ Max = nameMaxoutput__init__ called, self is: ⟨Dog B⟩max_dog ← ⟨Dog B⟩
16print()17max_dog→ ⟨Dog B⟩ = Dog("Max")1819print(f"\nbuddy is: {buddy⟨Dog A⟩}")20print(f"max_dog is: {max_dog⟨Dog B⟩}")2122# self becomes the object before the dot23print("\n=== self = Object Before the Dot ===")24buddy⟨Dog A⟩.bark() # self = buddy25print()output buddy is: ⟨Dog A⟩ max_dog is: ⟨Dog B⟩ === self = Object Before the Dot ===def bark(self):
pass 1 of 410def bark(self⟨Dog A⟩):11 print(f"bark() called, self is: {self}")12 print(f"{self.nameBuddy} says: Woof!")outputbark() called, self is: ⟨Dog A⟩ Buddy says: Woof!All 4 passes — pass 1 is the card above pass selfself.name1 ⟨Dog A⟩ Buddy 2 ⟨Dog B⟩ Max 3 ⟨Dog A⟩ Buddy 4 ⟨Dog A⟩ Buddy buddy.bark() # self = buddy
23print("\n=== self = Object Before the Dot ===")24buddy⟨Dog A⟩.bark() # self = buddy25print()26max_dog⟨Dog B⟩.bark() # self = max_dogmax_dog.bark() # self = max_dog
25print()26max_dog⟨Dog B⟩.bark() # self = max_dog2728print("\n=== Manual Call (Don't Do This) ===")2930# These are equivalent:31buddy⟨Dog A⟩.bark() # Normal way32Dog.bark(buddy) # What Python actually doesoutput === Manual Call (Don't Do This) ===buddy.bark() # Normal way
30# These are equivalent:31buddy⟨Dog A⟩.bark() # Normal way32Dog<class '__main__.Dog'>.bark(buddy⟨Dog A⟩) # What Python actually doesDog.bark(buddy) # What Python actually does
31buddy.bark() # Normal way32Dog<class '__main__.Dog'>.bark(buddy⟨Dog A⟩) # What Python actually does3334print("\n=== Each Object Has Its Own self ===")3536class Counter:37 def __init__(self, start=0):38 self.count = start39 40 def increment(self):41 self.count += 142 43 def display(self):44 print(f"This counter ({id(self)}): {self.count}")4546c1 = Counter(0)47c2 = Counter(10)output === Each Object Has Its Own self ===self.count ← 0
pass 1 of 236class Counter:37 def __init__(self⟨Counter C⟩, start0=0):38 self.count→ 0 = start0c1 ← ⟨Counter C⟩
46c1→ ⟨Counter C⟩ = Counter(0)47c2 = Counter(10)self.count ← 10
pass 2 of 236class Counter:37 def __init__(self⟨Counter D⟩, start10=0):38 self.count→ 10 = start10c2 ← ⟨Counter D⟩
46c1 = Counter(0)47c2→ ⟨Counter D⟩ = Counter(10)4849# Each operates on its own count50c1⟨Counter C⟩.increment()51c1.increment()self.count ← 1
pass 1 of 340def increment(self⟨Counter C⟩):41 self.count→ 1 += 1All 3 passes — pass 1 is the card above pass selfself.count1 ⟨Counter C⟩ 0 → 1 2 ⟨Counter C⟩ 1 → 2 3 ⟨Counter D⟩ 10 → 11 c1.increment()
49# Each operates on its own count50c1⟨Counter C⟩.increment()51c1⟨Counter C⟩.increment()52c2.increment()c1.increment()
50c1.increment()51c1⟨Counter C⟩.increment()52c2⟨Counter D⟩.increment()c2.increment()
51c1.increment()52c2⟨Counter D⟩.increment()5354c1⟨Counter C⟩.display() # self = c155c2.display() # self = c2def display(self):
pass 1 of 243def display(self⟨Counter C⟩):44 print(f"This counter ({id(self)}): {self.count2}")outputThis counter (129460874114352): 2c1.display() # self = c1
54c1⟨Counter C⟩.display() # self = c155c2⟨Counter D⟩.display() # self = c2def display(self):
pass 2 of 243def display(self⟨Counter D⟩):44 print(f"This counter ({id(self)}): {self.count11}")outputThis counter (129460874044400): 11obj ← ⟨Example E⟩
54c1.display() # self = c155c2⟨Counter D⟩.display() # self = c25657print("\n=== self is Just a Name ===")5859class Example:60 def method1(self):61 print(f"self = {self}")62 63 def method2(this):64 print(f"this = {this}") # Works! But don't do it65 66 def method3(potato):67 print(f"potato = {potato}") # Also works! Very bad style6869obj→ ⟨Example E⟩ = Example()70obj⟨Example E⟩.method1() # self71obj.method2() # this (same object)output === self is Just a Name ===def method1(self):
59class Example:60 def method1(self⟨Example E⟩):61 print(f"self = {self}")outputself = ⟨Example E⟩obj.method1() # self
69obj = Example()70obj⟨Example E⟩.method1() # self71obj⟨Example E⟩.method2() # this (same object)72obj.method3() # potato (same object)def method2(this):
63def method2(this⟨Example E⟩):64 print(f"this = {this⟨Example E⟩}") # Works! But don't do itoutputthis = ⟨Example E⟩obj.method2() # this (same object)
70obj.method1() # self71obj⟨Example E⟩.method2() # this (same object)72obj⟨Example E⟩.method3() # potato (same object)def method3(potato):
66def method3(potato⟨Example E⟩):67 print(f"potato = {potato⟨Example E⟩}") # Also works! Very bad styleoutputpotato = ⟨Example E⟩obj.method3() # potato (same object)
71obj.method2() # this (same object)72obj⟨Example E⟩.method3() # potato (same object)7374print("\nAlways use 'self' - it's Python convention!")output Always use 'self' - it's Python convention!
print("=== What is self? === ")
3print("=== What is self? ===\n")45class Dog:6 def __init__(self, name):7 print(f"__init__ called, self is: {self}")8 self.name = name9 10 def bark(self):11 print(f"bark() called, self is: {self}")12 print(f"{self.name} says: Woof!")1314# Create two dogs15buddy = Dog("Buddy")16print()output=== What is self? ===self.name ← Buddy
pass 1 of 25class Dog:6 def __init__(self⟨Dog A⟩, nameBuddy):7 print(f"__init__ called, self is: {self}")8 self.name→ Buddy = nameBuddyoutput__init__ called, self is: ⟨Dog A⟩buddy ← ⟨Dog A⟩
14# Create two dogs15buddy→ ⟨Dog A⟩ = Dog("Buddy")16print()17max_dog = Dog("Max")self.name ← Max
pass 2 of 25class Dog:6 def __init__(self⟨Dog B⟩, nameMax):7 print(f"__init__ called, self is: {self}")8 self.name→ Max = nameMaxoutput__init__ called, self is: ⟨Dog B⟩max_dog ← ⟨Dog B⟩
16print()17max_dog→ ⟨Dog B⟩ = Dog("Max")1819print(f"\nbuddy is: {buddy⟨Dog A⟩}")20print(f"max_dog is: {max_dog⟨Dog B⟩}")2122# self becomes the object before the dot23print("\n=== self = Object Before the Dot ===")24buddy⟨Dog A⟩.bark() # self = buddy25print()output buddy is: ⟨Dog A⟩ max_dog is: ⟨Dog B⟩ === self = Object Before the Dot ===def bark(self):
pass 1 of 410def bark(self⟨Dog A⟩):11 print(f"bark() called, self is: {self}")12 print(f"{self.nameBuddy} says: Woof!")outputbark() called, self is: ⟨Dog A⟩ Buddy says: Woof!All 4 passes — pass 1 is the card above pass selfself.name1 ⟨Dog A⟩ Buddy 2 ⟨Dog B⟩ Max 3 ⟨Dog A⟩ Buddy 4 ⟨Dog A⟩ Buddy buddy.bark() # self = buddy
23print("\n=== self = Object Before the Dot ===")24buddy⟨Dog A⟩.bark() # self = buddy25print()26max_dog⟨Dog B⟩.bark() # self = max_dogmax_dog.bark() # self = max_dog
25print()26max_dog⟨Dog B⟩.bark() # self = max_dog2728print("\n=== Manual Call (Don't Do This) ===")2930# These are equivalent:31buddy⟨Dog A⟩.bark() # Normal way32Dog.bark(buddy) # What Python actually doesoutput === Manual Call (Don't Do This) ===buddy.bark() # Normal way
30# These are equivalent:31buddy⟨Dog A⟩.bark() # Normal way32Dog<class '__main__.Dog'>.bark(buddy⟨Dog A⟩) # What Python actually doesDog.bark(buddy) # What Python actually does
31buddy.bark() # Normal way32Dog<class '__main__.Dog'>.bark(buddy⟨Dog A⟩) # What Python actually does3334print("\n=== Each Object Has Its Own self ===")3536class Counter:37 def __init__(self, start=0):38 self.count = start39 40 def increment(self):41 self.count += 142 43 def display(self):44 print(f"This counter ({id(self)}): {self.count}")4546c1 = Counter(0)47c2 = Counter(500)output === Each Object Has Its Own self ===self.count ← 0
pass 1 of 236class Counter:37 def __init__(self⟨Counter C⟩, start0=0):38 self.count→ 0 = start0c1 ← ⟨Counter C⟩
46c1→ ⟨Counter C⟩ = Counter(0)47c2 = Counter(500)self.count ← 500
pass 2 of 236class Counter:37 def __init__(self⟨Counter D⟩, start500=0):38 self.count→ 500 = start500c2 ← ⟨Counter D⟩
46c1 = Counter(0)47c2→ ⟨Counter D⟩ = Counter(500)4849# Each operates on its own count50c1⟨Counter C⟩.increment()51c1.increment()self.count ← 1
pass 1 of 340def increment(self⟨Counter C⟩):41 self.count→ 1 += 1All 3 passes — pass 1 is the card above pass selfself.count1 ⟨Counter C⟩ 0 → 1 2 ⟨Counter C⟩ 1 → 2 3 ⟨Counter D⟩ 500 → 501 c1.increment()
49# Each operates on its own count50c1⟨Counter C⟩.increment()51c1⟨Counter C⟩.increment()52c2.increment()c1.increment()
50c1.increment()51c1⟨Counter C⟩.increment()52c2⟨Counter D⟩.increment()c2.increment()
51c1.increment()52c2⟨Counter D⟩.increment()5354c1⟨Counter C⟩.display() # self = c155c2.display() # self = c2def display(self):
pass 1 of 243def display(self⟨Counter C⟩):44 print(f"This counter ({id(self)}): {self.count2}")outputThis counter (131536251368560): 2c1.display() # self = c1
54c1⟨Counter C⟩.display() # self = c155c2⟨Counter D⟩.display() # self = c2def display(self):
pass 2 of 243def display(self⟨Counter D⟩):44 print(f"This counter ({id(self)}): {self.count501}")outputThis counter (131536251298560): 501obj ← ⟨Example E⟩
54c1.display() # self = c155c2⟨Counter D⟩.display() # self = c25657print("\n=== self is Just a Name ===")5859class Example:60 def method1(self):61 print(f"self = {self}")62 63 def method2(this):64 print(f"this = {this}") # Works! But don't do it65 66 def method3(potato):67 print(f"potato = {potato}") # Also works! Very bad style6869obj→ ⟨Example E⟩ = Example()70obj⟨Example E⟩.method1() # self71obj.method2() # this (same object)output === self is Just a Name ===def method1(self):
59class Example:60 def method1(self⟨Example E⟩):61 print(f"self = {self}")outputself = ⟨Example E⟩obj.method1() # self
69obj = Example()70obj⟨Example E⟩.method1() # self71obj⟨Example E⟩.method2() # this (same object)72obj.method3() # potato (same object)def method2(this):
63def method2(this⟨Example E⟩):64 print(f"this = {this⟨Example E⟩}") # Works! But don't do itoutputthis = ⟨Example E⟩obj.method2() # this (same object)
70obj.method1() # self71obj⟨Example E⟩.method2() # this (same object)72obj⟨Example E⟩.method3() # potato (same object)def method3(potato):
66def method3(potato⟨Example E⟩):67 print(f"potato = {potato⟨Example E⟩}") # Also works! Very bad styleoutputpotato = ⟨Example E⟩obj.method3() # potato (same object)
71obj.method2() # this (same object)72obj⟨Example E⟩.method3() # potato (same object)7374print("\nAlways use 'self' - it's Python convention!")output Always use 'self' - it's Python convention!
self refers to the object the method was called on. Python passes it automatically.
Using self in methods
Access attributes and methods through self.
# Using self to Access Attributes and Methods
print("=== Accessing Attributes via self ===\n")
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
# Access attributes with self.
print(f"Hi, I'm {self.name} and I'm {self.age} years old.")
def have_birthday(self):
self.age += 1
print(f"Happy birthday {self.name}! Now {self.age} years old.")
def compare_age(self, other):
if self.age > other.age:
return f"{self.name} is older than {other.name}"
elif self.age < other.age:
return f"{self.name} is younger than {other.name}"
else:
return f"{self.name} and {other.name} are the same age"
alice = Person("Alice", 30)
bob = Person("Bob", 25)
alice.introduce()
bob.introduce()
alice.have_birthday()
print(alice.compare_age(bob))
print("\n=== Accessing Multiple Attributes ===")
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
def is_square(self):
return self.width == self.height
def resize(self, factor):
self.width *= factor
self.height *= factor
def describe(self):
shape = "square" if self.is_square() else "rectangle"
return f"{shape} {self.width}x{self.height}, area={self.area()}"
rect = Rectangle(4, 4)
print(rect.describe())
rect.resize(2)
print(rect.describe())
print("\n=== self in Chained Operations ===")
class BankAccount:
def __init__(self, balance=0):
self.balance = balance
self.transactions = []
def deposit(self, amount):
self.balance += amount
self.transactions.append(f"+{amount}")
return self
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
self.transactions.append(f"-{amount}")
return self
def display(self):
print(f"Balance: ${self.balance}")
print(f"Transactions: {self.transactions}")
return self
# Chain method calls
account = BankAccount(100)
account.deposit(50).deposit(25).withdraw(30).display()
print("\n=== self Refers to Same Object ===")
class IdentityChecker:
def __init__(self, name):
self.name = name
def check_identity(self, other_obj):
# Compare if self and other_obj are same object
if self is other_obj:
print(f"self IS the same object as {other_obj.name}")
else:
print(f"self is DIFFERENT from {other_obj.name}")
obj1 = IdentityChecker("Object1")
obj2 = IdentityChecker("Object2")
obj1.check_identity(obj1) # Same object
obj1.check_identity(obj2) # Different objects
print("\n=== self in String Methods ===")
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
def __str__(self):
return f"'{self.title}' by {self.author}"
def __repr__(self):
return f"Book({self.title!r}, {self.author!r}, {self.pages})"
book = Book("Python Guide", "John Doe", 300)
print(book) # Calls __str__
print(repr(book)) # Calls __repr__
print("=== Accessing Attributes via self === ")
3print("=== Accessing Attributes via self ===\n")45class Person:6 def __init__(self, name, age):7 self.name = name #?createattr8 self.age = age9 10 def introduce(self):11 # Access attributes with self. #?accessattr12 print(f"Hi, I'm {self.name} and I'm {self.age} years old.")13 14 def have_birthday(self):15 self.age += 1 #?modifyattr16 print(f"Happy birthday {self.name}! Now {self.age} years old.")17 18 def compare_age(self, other): #?compareother19 if self.age > other.age:20 return f"{self.name} is older than {other.name}"21 elif self.age < other.age:22 return f"{self.name} is younger than {other.name}"23 else:24 return f"{self.name} and {other.name} are the same age"2526alice = Person("Alice", 30)27bob = Person("Bob", 25)output=== Accessing Attributes via self ===self.name ← Alice, self.age ← 30
pass 1 of 25class Person:6 def __init__(self⟨Person A⟩, nameAlice, age30):7 self.name→ Alice = nameAlice #?createattr8 self.age→ 30 = age30alice ← ⟨Person A⟩
26alice→ ⟨Person A⟩ = Person("Alice", 30)27bob = Person("Bob", 25)self.name ← Bob, self.age ← 25
pass 2 of 25class Person:6 def __init__(self⟨Person B⟩, nameBob, age25):7 self.name→ Bob = nameBob #?createattr8 self.age→ 25 = age25bob ← ⟨Person B⟩
26alice = Person("Alice", 30)27bob→ ⟨Person B⟩ = Person("Bob", 25)2829alice⟨Person A⟩.introduce()30bob.introduce()def introduce(self): # Access attributes with self. #?accessa…
pass 1 of 210def introduce(self⟨Person A⟩):11 # Access attributes with self. #?accessattr12 print(f"Hi, I'm {self.nameAlice} and I'm {self.age30} years old.")outputHi, I'm Alice and I'm 30 years old.alice.introduce()
29alice⟨Person A⟩.introduce()30bob⟨Person B⟩.introduce()31alice.have_birthday()def introduce(self): # Access attributes with self. #?accessa…
pass 2 of 210def introduce(self⟨Person B⟩):11 # Access attributes with self. #?accessattr12 print(f"Hi, I'm {self.nameBob} and I'm {self.age25} years old.")outputHi, I'm Bob and I'm 25 years old.bob.introduce()
29alice.introduce()30bob⟨Person B⟩.introduce()31alice⟨Person A⟩.have_birthday()32print(alice.compare_age(bob))self.age ← 31
14def have_birthday(self⟨Person A⟩):15 self.age→ 31 += 1 #?modifyattr16 print(f"Happy birthday {self.nameAlice}! Now {self.age31} years old.")outputHappy birthday Alice! Now 31 years old.alice.have_birthday()
30bob.introduce()31alice⟨Person A⟩.have_birthday()32print(alice⟨Person A⟩.compare_age(bob⟨Person B⟩))def compare_age(self, other): #?compareother
18def compare_age(self⟨Person A⟩, other⟨Person B⟩): #?compareother19 if self.age > other.age:20 return f"{self.name} is older than {other.name}"if self.age > other.age:
18def compare_age(self, other): #?compareother19 if self.age31 > other.age25:20 return f"{self.nameAlice} is older than {other.nameBob}"21 elif self.age < other.age:print(alice.compare_age(bob))
31alice.have_birthday()32print(alice⟨Person A⟩.compare_age(bob⟨Person B⟩))3334print("\n=== Accessing Multiple Attributes ===")3536class Rectangle:37 def __init__(self, width, height):38 self.width = width39 self.height = height40 41 def area(self):42 return self.width * self.height #?multiattr43 44 def perimeter(self):45 return 2 * (self.width + self.height)46 47 def is_square(self):48 return self.width == self.height #?compareattrs49 50 def resize(self, factor):51 self.width *= factor #?modifymulti52 self.height *= factor53 54 def describe(self):55 shape = "square" if self.is_square() else "rectangle" #?callmethod56 return f"{shape} {self.width}x{self.height}, area={self.area()}"5758rect = Rectangle(4, 4)59print(rect.describe())outputAlice is older than Bob === Accessing Multiple Attributes ===self.width ← 4, self.height ← 4
36class Rectangle:37 def __init__(self⟨Rectangle C⟩, width4, height4):38 self.width→ 4 = width439 self.height→ 4 = height4rect ← ⟨Rectangle C⟩
58rect→ ⟨Rectangle C⟩ = Rectangle(4, 4)59print(rect⟨Rectangle C⟩.describe())60rect.resize(2)def describe(self):
pass 1 of 254def describe(self⟨Rectangle C⟩):55 shape = "square" if self.is_square() else "rectangle" #?callmethod56 return f"{shape} {self.width}x{self.height}, area={self.area()}"def is_square(self):
pass 1 of 247def is_square(self⟨Rectangle C⟩):48 return self.width4 == self.height4 #?compareattrsshape ← square
54def describe(self):55 shape→ square = "square" if self.is_square() else "rectangle" #?callmethod56 return f"{shapesquare} {self.width4}x{self.height4}, area={self.area()}"def area(self):
pass 1 of 241def area(self⟨Rectangle C⟩):42 return self.width4 * self.height4 #?multiattrprint(rect.describe())
58rect = Rectangle(4, 4)59print(rect⟨Rectangle C⟩.describe())60rect⟨Rectangle C⟩.resize(2)61print(rect.describe())outputsquare 4x4, area=16self.width ← 8, self.height ← 8
50def resize(self⟨Rectangle C⟩, factor2):51 self.width→ 8 *= factor2 #?modifymulti52 self.height→ 8 *= factor2rect.resize(2)
59print(rect.describe())60rect⟨Rectangle C⟩.resize(2)61print(rect⟨Rectangle C⟩.describe())def describe(self):
pass 2 of 254def describe(self⟨Rectangle C⟩):55 shape = "square" if self.is_square() else "rectangle" #?callmethod56 return f"{shape} {self.width}x{self.height}, area={self.area()}"def is_square(self):
pass 2 of 247def is_square(self⟨Rectangle C⟩):48 return self.width8 == self.height8 #?compareattrsshape ← square
54def describe(self):55 shape→ square = "square" if self.is_square() else "rectangle" #?callmethod56 return f"{shapesquare} {self.width8}x{self.height8}, area={self.area()}"def area(self):
pass 2 of 241def area(self⟨Rectangle C⟩):42 return self.width8 * self.height8 #?multiattrprint(rect.describe())
60rect.resize(2)61print(rect⟨Rectangle C⟩.describe())6263print("\n=== self in Chained Operations ===")6465class BankAccount:66 def __init__(self, balance=0):67 self.balance = balance68 self.transactions = []69 70 def deposit(self, amount):71 self.balance += amount72 self.transactions.append(f"+{amount}") #?appendlist73 return self #?returnself74 75 def withdraw(self, amount):76 if amount <= self.balance:77 self.balance -= amount78 self.transactions.append(f"-{amount}")79 return self80 81 def display(self):82 print(f"Balance: ${self.balance}")83 print(f"Transactions: {self.transactions}")84 return self8586# Chain method calls #?chaining87account = BankAccount(100)88account.deposit(50).deposit(25).withdraw(30).display()outputsquare 8x8, area=64 === self in Chained Operations ===self.balance ← 100, self.transactions ← []
65class BankAccount:66 def __init__(self⟨BankAccount D⟩, balance100=0):67 self.balance→ 100 = balance10068 self.transactions→ [] = []account ← ⟨BankAccount D⟩
86# Chain method calls #?chaining87account→ ⟨BankAccount D⟩ = BankAccount(100)88account⟨BankAccount D⟩.deposit(50).deposit(25).withdraw(30).display()self.balance ← 150, self.transactions ← ['+50']
pass 1 of 270def deposit(self⟨BankAccount D⟩, amount50):71 self.balance→ 150 += amount5072 self.transactions→ ['+50'].append(f"+{amount50}") #?appendlist73 return self #?returnselfself.balance ← 175, self.transactions ← ['+50', '+25']
pass 2 of 270def deposit(self⟨BankAccount D⟩, amount25):71 self.balance→ 175 += amount2572 self.transactions→ ['+50', '+25'].append(f"+{amount25}") #?appendlist73 return self #?returnselfdef withdraw(self, amount):
75def withdraw(self⟨BankAccount D⟩, amount30):76 if amount <= self.balance:77 self.balance -= amountself.balance ← 145, self.transactions ← ['+50', '+25', '-30']
75def withdraw(self, amount):76 if amount30 <= self.balance175:77 self.balance→ 145 -= amount3078 self.transactions→ ['+50', '+25', '-30'].append(f"-{amount30}")79 return selfreturn self
78 self.transactions.append(f"-{amount}")79return selfdef display(self):
81def display(self⟨BankAccount D⟩):82 print(f"Balance: ${self.balance145}")83 print(f"Transactions: {self.transactions['+50', '+25', '-30']}")84 return selfoutputBalance: $145 Transactions: ['+50', '+25', '-30']account.deposit(50).deposit(25).withdraw(30).display()
87account = BankAccount(100)88account⟨BankAccount D⟩.deposit(50).deposit(25).withdraw(30).display()8990print("\n=== self Refers to Same Object ===")9192class IdentityChecker:93 def __init__(self, name):94 self.name = name95 96 def check_identity(self, other_obj):97 # Compare if self and other_obj are same object #?identitycheck98 if self is other_obj:99 print(f"self IS the same object as {other_obj.name}")100 else:101 print(f"self is DIFFERENT from {other_obj.name}")102103obj1 = IdentityChecker("Object1")104obj2 = IdentityChecker("Object2")output === self Refers to Same Object ===self.name ← Object1
pass 1 of 292class IdentityChecker:93 def __init__(self⟨IdentityChecker E⟩, nameObject1):94 self.name→ Object1 = nameObject1obj1 ← ⟨IdentityChecker E⟩
103obj1→ ⟨IdentityChecker E⟩ = IdentityChecker("Object1")104obj2 = IdentityChecker("Object2")self.name ← Object2
pass 2 of 292class IdentityChecker:93 def __init__(self⟨IdentityChecker F⟩, nameObject2):94 self.name→ Object2 = nameObject2obj2 ← ⟨IdentityChecker F⟩
103obj1 = IdentityChecker("Object1")104obj2→ ⟨IdentityChecker F⟩ = IdentityChecker("Object2")105106obj1⟨IdentityChecker E⟩.check_identity(obj1) # Same object107obj1.check_identity(obj2) # Different objectsdef check_identity(self, other_obj): # Compare if self and oth…
pass 1 of 296def check_identity(self⟨IdentityChecker E⟩, other_obj⟨IdentityChecker E⟩):97 # Compare if self and other_obj are same object #?identitycheck98 if self is other_obj:99 print(f"self IS the same object as {other_obj.name}")if self is other_obj:
97# Compare if self and other_obj are same object #?identitycheck98if self is other_obj⟨IdentityChecker E⟩:99 print(f"self IS the same object as {other_obj.nameObject1}")100else:outputself IS the same object as Object1obj1.check_identity(obj1) # Same object
106obj1⟨IdentityChecker E⟩.check_identity(obj1) # Same object107obj1⟨IdentityChecker E⟩.check_identity(obj2⟨IdentityChecker F⟩) # Different objectsdef check_identity(self, other_obj): # Compare if self and oth…
pass 2 of 296def check_identity(self⟨IdentityChecker E⟩, other_obj⟨IdentityChecker F⟩):97 # Compare if self and other_obj are same object #?identitycheck98 if self is other_obj:99 print(f"self IS the same object as {other_obj.name}")else:
98if self is other_obj:99 print(f"self IS the same object as {other_obj.name}")100else:101 print(f"self is DIFFERENT from {other_obj.nameObject2}")outputself is DIFFERENT from Object2obj1.check_identity(obj2) # Different objects
106obj1.check_identity(obj1) # Same object107obj1⟨IdentityChecker E⟩.check_identity(obj2⟨IdentityChecker F⟩) # Different objects108109print("\n=== self in String Methods ===")110111class Book:112 def __init__(self, title, author, pages):113 self.title = title114 self.author = author115 self.pages = pages116 117 def __str__(self): #?strmethod118 return f"'{self.title}' by {self.author}"119 120 def __repr__(self): #?reprmethod121 return f"Book({self.title!r}, {self.author!r}, {self.pages})"122123book = Book("Python Guide", "John Doe", 300)124print(book) # Calls __str__output === self in String Methods ===self.title ← Python Guide, self.author ← John Doe, self.pages ← 300
111class Book:112 def __init__(self(empty), titlePython Guide, authorJohn Doe, pages300):113 self.title→ Python Guide = titlePython Guide114 self.author→ John Doe = authorJohn Doe115 self.pages→ 300 = pages300book ← 'Python Guide' by John Doe
123book→ 'Python Guide' by John Doe = Book("Python Guide", "John Doe", 300)124print(book'Python Guide' by John Doe) # Calls __str__125print(repr(book'Python Guide' by John Doe)) # Calls __repr__126#@help createattroutput'Python Guide' by John Doe Book('Python Guide', 'John Doe', 300)
self.name accesses attribute. self.other_method() calls another method.
self vs class attributes
Instance attributes vs class-level attributes.
# Instance (self) vs Class Attributes
print("=== Class Attributes ===\n")
class Dog:
species = "Canis familiaris"
count = 0
def __init__(self, name):
self.name = name
Dog.count += 1
# Create dogs
buddy = Dog("Buddy")
max_dog = Dog("Max")
rex = Dog("Rex")
# Class attribute - shared
print(f"Dog.species: {Dog.species}")
print(f"buddy.species: {buddy.species}") # Access via instance
print(f"max_dog.species: {max_dog.species}") # Same value
# Instance attribute - unique
print(f"\nbuddyname: {buddy.name}")
print(f"max_dog.name: {max_dog.name}")
# Count is shared
print(f"\nTotal dogs created: {Dog.count}")
print("\n=== Modifying Class vs Instance ===")
class Cat:
species = "Felis catus"
def __init__(self, name):
self.name = name
whiskers = Cat("Whiskers")
luna = Cat("Luna")
print(f"Before: whiskers.species = {whiskers.species}")
print(f"Before: luna.species = {luna.species}")
# Modify class attribute
Cat.species = "Felis silvestris catus"
print(f"\nAfter Cat.species change:")
print(f"whiskers.species = {whiskers.species}") # Changed!
print(f"luna.species = {luna.species}") # Changed!
# Modify on instance - creates instance attribute!
whiskers.species = "Special Cat"
print(f"\nAfter whiskers.species = 'Special Cat':")
print(f"whiskers.species = {whiskers.species}") # Instance attr
print(f"luna.species = {luna.species}") # Still class attr
print(f"Cat.species = {Cat.species}") # Class attr unchanged
print("\n=== self vs Class in Methods ===")
class Counter:
total = 0 # Class attribute
def __init__(self, name):
self.name = name # Instance attribute
self.count = 0 # Instance attribute
Counter.total += 1
def increment(self):
self.count += 1
Counter.total += 1 # Or self.__class__.total
def display(self):
print(f"{self.name}: count={self.count}, total={Counter.total}")
c1 = Counter("Counter1")
c2 = Counter("Counter2")
c1.increment()
c1.increment()
c2.increment()
c1.display()
c2.display()
print("\n=== When to Use Each ===")
class Player:
# Class attributes: shared by all
max_level = 100
all_players = []
def __init__(self, name, level=1):
# Instance attributes: unique to each
self.name = name
self.level = level
Player.all_players.append(self)
def level_up(self):
if self.level < Player.max_level:
self.level += 1
@classmethod
def get_player_count(cls):
return len(cls.all_players)
p1 = Player("Alice", 10)
p2 = Player("Bob", 5)
print(f"Max level (class): {Player.max_level}")
print(f"Alice's level (instance): {p1.level}")
print(f"Bob's level (instance): {p2.level}")
print(f"Total players: {Player.get_player_count()}")
p1.level_up()
print(f"After level up - Alice: {p1.level}, Bob: {p2.level}")
print("\n=== Summary ===")
print("""
Class Attribute:
- Defined in class body (not __init__)
- Shared by ALL instances
- Access: ClassName.attr or self.attr
- Modify: Use ClassName.attr
Instance Attribute:
- Defined with self.attr = value
- Unique to EACH instance
- Access: self.attr
- Modify: self.attr = new_value
""")
species ← (empty), count ← (empty)
3print("=== Class Attributes ===\n")45class Dog:6 species→ (empty) = "Canis familiaris" #?classattr7 count→ (empty) = 0 #?classcount8 9 def __init__(self, name):10 self.name = name #?instanceattr11 Dog.count += 1 #?incrementclass1213# Create dogs14buddy = Dog("Buddy")15max_dog = Dog("Max")output=== Class Attributes ===self.name ← Buddy, Dog.count ← 1
pass 1 of 39def __init__(self⟨Dog A⟩, nameBuddy):10 self.name→ Buddy = nameBuddy #?instanceattr11 Dog.count→ 1 += 1 #?incrementclassAll 3 passes — pass 1 is the card above pass selfnameself.nameDog.count1 ⟨Dog A⟩ Buddy Buddy 0 → 1 2 ⟨Dog B⟩ Max Max 1 → 2 3 ⟨Dog C⟩ Rex Rex 2 → 3 buddy ← ⟨Dog A⟩
13# Create dogs14buddy→ ⟨Dog A⟩ = Dog("Buddy")15max_dog = Dog("Max")16rex = Dog("Rex")max_dog ← ⟨Dog B⟩
14buddy = Dog("Buddy")15max_dog→ ⟨Dog B⟩ = Dog("Max")16rex = Dog("Rex")rex ← ⟨Dog C⟩, species ← (empty)
15max_dog = Dog("Max")16rex→ ⟨Dog C⟩ = Dog("Rex")1718# Class attribute - shared #?sharedaccess19print(f"Dog.species: {Dog.speciesCanis familiaris}")20print(f"buddy.species: {buddy.speciesCanis familiaris}") # Access via instance21print(f"max_dog.species: {max_dog.speciesCanis familiaris}") # Same value2223# Instance attribute - unique #?uniqueaccess24print(f"\nbuddyname: {buddy.nameBuddy}")25print(f"max_dog.name: {max_dog.nameMax}")2627# Count is shared #?sharedcount28print(f"\nTotal dogs created: {Dog.count3}")2930print("\n=== Modifying Class vs Instance ===")3132class Cat:33 species→ (empty) = "Felis catus"34 35 def __init__(self, name):36 self.name = name3738whiskers = Cat("Whiskers")39luna = Cat("Luna")outputDog.species: Canis familiaris buddy.species: Canis familiaris max_dog.species: Canis familiaris buddyname: Buddy max_dog.name: Max Total dogs created: 3 === Modifying Class vs Instance ===self.name ← Whiskers
pass 1 of 235def __init__(self⟨Cat D⟩, nameWhiskers):36 self.name→ Whiskers = nameWhiskerswhiskers ← ⟨Cat D⟩
38whiskers→ ⟨Cat D⟩ = Cat("Whiskers")39luna = Cat("Luna")self.name ← Luna
pass 2 of 235def __init__(self⟨Cat E⟩, nameLuna):36 self.name→ Luna = nameLunaluna ← ⟨Cat E⟩, Cat.species ← Felis silvestris catus, whiskers.species ← Special Cat
38whiskers = Cat("Whiskers")39luna→ ⟨Cat E⟩ = Cat("Luna")4041print(f"Before: whiskers.species = {whiskers.speciesFelis catus}")42print(f"Before: luna.species = {luna.speciesFelis catus}")4344# Modify class attribute #?modifyclass45Cat.species→ Felis silvestris catus = "Felis silvestris catus"46print(f"\nAfter Cat.species change:")47print(f"whiskers.species = {whiskers.speciesFelis silvestris catus}") # Changed!48print(f"luna.species = {luna.speciesFelis silvestris catus}") # Changed!4950# Modify on instance - creates instance attribute! #?shadowclass51whiskers.species→ Special Cat = "Special Cat" #?creates_instance_attr52print(f"\nAfter whiskers.species = 'Special Cat':")53print(f"whiskers.species = {whiskers.speciesSpecial Cat}") # Instance attr54print(f"luna.species = {luna.speciesFelis silvestris catus}") # Still class attr55print(f"Cat.species = {Cat.speciesFelis silvestris catus}") # Class attr unchanged5657print("\n=== self vs Class in Methods ===")5859class Counter:60 total→ (empty) = 0 # Class attribute #?classtotal61 62 def __init__(self, name):63 self.name = name # Instance attribute #?selfname64 self.count = 0 # Instance attribute65 Counter.total += 166 67 def increment(self):68 self.count += 1 #?selfincrement69 Counter.total += 1 # Or self.__class__.total #?classincrement70 71 def display(self):72 print(f"{self.name}: count={self.count}, total={Counter.total}")7374c1 = Counter("Counter1")75c2 = Counter("Counter2")outputBefore: whiskers.species = Felis catus Before: luna.species = Felis catus After Cat.species change: whiskers.species = Felis silvestris catus luna.species = Felis silvestris catus After whiskers.species = 'Special Cat': whiskers.species = Special Cat luna.species = Felis silvestris catus Cat.species = Felis silvestris catus === self vs Class in Methods ===self.name ← Counter1, self.count ← 0, Counter.total ← 1
pass 1 of 262def __init__(self⟨Counter F⟩, nameCounter1):63 self.name→ Counter1 = nameCounter1 # Instance attribute #?selfname64 self.count→ 0 = 0 # Instance attribute65 Counter.total→ 1 += 1c1 ← ⟨Counter F⟩
74c1→ ⟨Counter F⟩ = Counter("Counter1")75c2 = Counter("Counter2")self.name ← Counter2, self.count ← 0, Counter.total ← 2
pass 2 of 262def __init__(self⟨Counter G⟩, nameCounter2):63 self.name→ Counter2 = nameCounter2 # Instance attribute #?selfname64 self.count→ 0 = 0 # Instance attribute65 Counter.total→ 2 += 1c2 ← ⟨Counter G⟩
74c1 = Counter("Counter1")75c2→ ⟨Counter G⟩ = Counter("Counter2")7677c1⟨Counter F⟩.increment()78c1.increment()self.count ← 1, Counter.total ← 3
pass 1 of 367def increment(self⟨Counter F⟩):68 self.count→ 1 += 1 #?selfincrement69 Counter.total→ 3 += 1 # Or self.__class__.total #?classincrementAll 3 passes — pass 1 is the card above pass selfself.countCounter.total1 ⟨Counter F⟩ 0 → 1 2 → 3 2 ⟨Counter F⟩ 1 → 2 3 → 4 3 ⟨Counter G⟩ 0 → 1 4 → 5 c1.increment()
77c1⟨Counter F⟩.increment()78c1⟨Counter F⟩.increment()79c2.increment()c1.increment()
77c1.increment()78c1⟨Counter F⟩.increment()79c2⟨Counter G⟩.increment()c2.increment()
78c1.increment()79c2⟨Counter G⟩.increment()8081c1⟨Counter F⟩.display()82c2.display()def display(self):
pass 1 of 271def display(self⟨Counter F⟩):72 print(f"{self.nameCounter1}: count={self.count2}, total={Counter.total5}")outputCounter1: count=2, total=5c1.display()
81c1⟨Counter F⟩.display()82c2⟨Counter G⟩.display()def display(self):
pass 2 of 271def display(self⟨Counter G⟩):72 print(f"{self.nameCounter2}: count={self.count1}, total={Counter.total5}")outputCounter2: count=1, total=5max_level ← (empty), all_players ← (empty)
81c1.display()82c2⟨Counter G⟩.display()8384print("\n=== When to Use Each ===")8586class Player:87 # Class attributes: shared by all #?whenshared88 max_level→ (empty) = 10089 all_players→ (empty) = []90 91 def __init__(self, name, level=1):92 # Instance attributes: unique to each #?whenunique93 self.name = name94 self.level = level95 Player.all_players.append(self) #?trackinlist96 97 def level_up(self):98 if self.level < Player.max_level: #?accessboth99 self.level += 1100 101 @classmethod102 def get_player_count(cls): #?classmethod103 return len(cls.all_players)104105p1 = Player("Alice", 10)106p2 = Player("Bob", 5)output === When to Use Each ===self.name ← Alice, self.level ← 10, Player.all_players ← [⟨Player H⟩]
pass 1 of 291def __init__(self⟨Player H⟩, nameAlice, level10=1):92 # Instance attributes: unique to each #?whenunique93 self.name→ Alice = nameAlice94 self.level→ 10 = level1095 Player.all_players→ [⟨Player H⟩].append(self) #?trackinlistp1 ← ⟨Player H⟩
105p1→ ⟨Player H⟩ = Player("Alice", 10)106p2 = Player("Bob", 5)self.name ← Bob, self.level ← 5, Player.all_players ← [⟨Player H⟩, ⟨Player I⟩]
pass 2 of 291def __init__(self⟨Player I⟩, nameBob, level5=1):92 # Instance attributes: unique to each #?whenunique93 self.name→ Bob = nameBob94 self.level→ 5 = level595 Player.all_players→ [⟨Player H⟩, ⟨Player I⟩].append(self) #?trackinlistp2 ← ⟨Player I⟩
105p1 = Player("Alice", 10)106p2→ ⟨Player I⟩ = Player("Bob", 5)107108print(f"Max level (class): {Player.max_level100}")109print(f"Alice's level (instance): {p1.level10}")110print(f"Bob's level (instance): {p2.level5}")111print(f"Total players: {Player<class '__main__.Player'>.get_player_count()}")outputMax level (class): 100 Alice's level (instance): 10 Bob's level (instance): 5def get_player_count(cls): #?classmethod
101@classmethod102def get_player_count(cls<class '__main__.Player'>): #?classmethod103 return len(cls.all_players[⟨Player H⟩, ⟨Player I⟩])print(f"Total players: {Player.get_player_count()}")
110print(f"Bob's level (instance): {p2.level}")111print(f"Total players: {Player<class '__main__.Player'>.get_player_count()}")112113p1⟨Player H⟩.level_up()114print(f"After level up - Alice: {p1.level}, Bob: {p2.level}")outputTotal players: 2def level_up(self):
97def level_up(self⟨Player H⟩):98 if self.level < Player.max_level: #?accessboth99 self.level += 1self.level ← 11
97def level_up(self):98 if self.level10 < Player.max_level100: #?accessboth99 self.level→ 11 += 1p1.level_up()
113p1⟨Player H⟩.level_up()114print(f"After level up - Alice: {p1.level11}, Bob: {p2.level5}")115116print("\n=== Summary ===")117print("""118Class Attribute:119 - Defined in class body (not __init__)120 - Shared by ALL instances121 - Access: ClassName.attr or self.attr122 - Modify: Use ClassName.attr123124Instance Attribute:125 - Defined with self.attr = value126 - Unique to EACH instance127 - Access: self.attr128 - Modify: self.attr = new_value129""")130#@help classattroutputAfter level up - Alice: 11, Bob: 5 === Summary === Class Attribute: - Defined in class body (not __init__) - Shared by ALL instances - Access: ClassName.attr or self.attr - Modify: Use ClassName.attr Instance Attribute: - Defined with self.attr = value - Unique to EACH instance - Access: self.attr - Modify: self.attr = new_value
self.x is per-instance. ClassName.x or bare x in class body is shared.
Calling other methods
Use self to invoke other methods on the same object.
# Using self to Call Other Methods
print("=== Calling Methods via self ===\n")
class Calculator:
def __init__(self, value=0):
self.value = value
def add(self, n):
self.value += n
return self
def subtract(self, n):
self.value -= n
return self
def multiply(self, n):
self.value *= n
return self
def square(self):
self.multiply(self.value) # Call self.multiply()
return self
def double(self):
self.multiply(2)
return self
calc = Calculator(5)
calc.square() # 5 * 5 = 25
print(f"5 squared = {calc.value}")
calc = Calculator(3)
calc.double().double() # 3 * 2 * 2 = 12
print(f"3 doubled twice = {calc.value}")
print("\n=== Helper Methods ===")
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, name, price, qty=1):
self.items.append({"name": name, "price": price, "qty": qty})
self._log_action(f"Added {qty}x {name}")
def remove_item(self, name):
for item in self.items:
if item["name"] == name:
self.items.remove(item)
self._log_action(f"Removed {name}")
return
self._log_action(f"Not found: {name}")
def _log_action(self, message):
print(f"[Cart] {message}")
def _calculate_subtotal(self):
return sum(i["price"] * i["qty"] for i in self.items)
def get_total(self):
subtotal = self._calculate_subtotal()
return subtotal
cart = ShoppingCart()
cart.add_item("Apple", 1.50, 3)
cart.add_item("Bread", 2.50)
cart.remove_item("Milk") # Not found
print(f"Total: ${cart.get_total():.2f}")
print("\n=== Validation Methods ===")
class User:
def __init__(self, username, email, age):
self.username = username
self.email = email
self.age = age
def _is_valid_username(self):
return len(self.username) >= 3
def _is_valid_email(self):
return "@" in self.email and "." in self.email
def _is_valid_age(self):
return 0 <= self.age <= 150
def is_valid(self):
return (self._is_valid_username() and
self._is_valid_email() and
self._is_valid_age())
def validation_errors(self):
errors = []
if not self._is_valid_username():
errors.append("Username too short")
if not self._is_valid_email():
errors.append("Invalid email format")
if not self._is_valid_age():
errors.append("Invalid age")
return errors
user1 = User("alice", "alice@email.com", 25)
user2 = User("ab", "not-an-email", 200)
print(f"User1 valid: {user1.is_valid()}")
print(f"User2 valid: {user2.is_valid()}")
print(f"User2 errors: {user2.validation_errors()}")
print("\n=== State Management ===")
class Player:
def __init__(self, name, health=100):
self.name = name
self.health = health
self.alive = True
def take_damage(self, amount):
self.health -= amount
print(f"{self.name} takes {amount} damage! Health: {self.health}")
self._check_death()
def heal(self, amount):
if self.alive:
self.health = min(100, self.health + amount)
print(f"{self.name} heals {amount}! Health: {self.health}")
def _check_death(self):
if self.health <= 0 and self.alive:
self.alive = False
self._on_death()
def _on_death(self):
print(f"{self.name} has fallen!")
hero = Player("Hero")
hero.take_damage(30)
hero.take_damage(50)
hero.take_damage(30) # Dies
hero.heal(20) # Can't heal when dead
print("\n=== Method Delegation ===")
class Report:
def __init__(self, title, data):
self.title = title
self.data = data
def generate(self, format="text"):
if format == "text":
return self._generate_text()
elif format == "html":
return self._generate_html()
else:
return self._generate_text()
def _generate_text(self):
lines = [f"=== {self.title} ==="]
for key, value in self.data.items():
lines.append(f"{key}: {value}")
return "\n".join(lines)
def _generate_html(self):
html = [f"<h1>{self.title}</h1>", "<ul>"]
for key, value in self.data.items():
html.append(f" <li>{key}: {value}</li>")
html.append("</ul>")
return "\n".join(html)
report = Report("Sales", {"Q1": 1000, "Q2": 1500, "Q3": 1200})
print(report.generate("text"))
print()
print(report.generate("html"))
print("=== Calling Methods via self === ")
3print("=== Calling Methods via self ===\n")45class Calculator:6 def __init__(self, value=0):7 self.value = value8 9 def add(self, n):10 self.value += n11 return self12 13 def subtract(self, n):14 self.value -= n15 return self16 17 def multiply(self, n):18 self.value *= n19 return self20 21 def square(self): #?callself22 self.multiply(self.value) # Call self.multiply() #?selfcall23 return self24 25 def double(self):26 self.multiply(2) #?reuse27 return self2829calc = Calculator(5)30calc.square() # 5 * 5 = 25output=== Calling Methods via self ===self.value ← 5
pass 1 of 25class Calculator:6 def __init__(self⟨Calculator A⟩, value5=0):7 self.value→ 5 = value5calc ← ⟨Calculator A⟩
29calc→ ⟨Calculator A⟩ = Calculator(5)30calc⟨Calculator A⟩.square() # 5 * 5 = 2531print(f"5 squared = {calc.value}")def square(self): #?callself
21def square(self⟨Calculator A⟩): #?callself22 self.multiply(self.value5) # Call self.multiply() #?selfcall23 return selfself.value ← 25
pass 1 of 317def multiply(self⟨Calculator A⟩, n5):18 self.value→ 25 *= n519 return selfAll 3 passes — pass 1 is the card above pass selfnself.value1 ⟨Calculator A⟩ 5 5 → 25 2 ⟨Calculator B⟩ 2 3 → 6 3 ⟨Calculator B⟩ 2 6 → 12 self.value ← 25
21def square(self): #?callself22 self.multiply(self.value→ 25) # Call self.multiply() #?selfcall23 return selfcalc.square() # 5 * 5 = 25
29calc = Calculator(5)30calc⟨Calculator A⟩.square() # 5 * 5 = 2531print(f"5 squared = {calc.value25}")3233calc = Calculator(3)34calc.double().double() # 3 * 2 * 2 = 12output5 squared = 25self.value ← 3
pass 2 of 25class Calculator:6 def __init__(self⟨Calculator B⟩, value3=0):7 self.value→ 3 = value3calc ← ⟨Calculator B⟩
33calc→ ⟨Calculator B⟩ = Calculator(3)34calc⟨Calculator B⟩.double().double() # 3 * 2 * 2 = 1235print(f"3 doubled twice = {calc.value}")def double(self):
pass 1 of 225def double(self⟨Calculator B⟩):26 self.multiply(2) #?reuse27 return selfself.multiply(2) #?reuse
25def double(self):26 self.multiply(2) #?reuse27 return selfdef double(self):
pass 2 of 225def double(self⟨Calculator B⟩):26 self.multiply(2) #?reuse27 return selfself.multiply(2) #?reuse
25def double(self):26 self.multiply(2) #?reuse27 return selfcalc.double().double() # 3 * 2 * 2 = 12
33calc = Calculator(3)34calc⟨Calculator B⟩.double().double() # 3 * 2 * 2 = 1235print(f"3 doubled twice = {calc.value12}")3637print("\n=== Helper Methods ===")3839class ShoppingCart:40 def __init__(self):41 self.items = []42 43 def add_item(self, name, price, qty=1):44 self.items.append({"name": name, "price": price, "qty": qty})45 self._log_action(f"Added {qty}x {name}") #?privatehelper46 47 def remove_item(self, name):48 for item in self.items:49 if item["name"] == name:50 self.items.remove(item)51 self._log_action(f"Removed {name}")52 return53 self._log_action(f"Not found: {name}")54 55 def _log_action(self, message): #?helpermethod56 print(f"[Cart] {message}")57 58 def _calculate_subtotal(self): #?calchelper59 return sum(i["price"] * i["qty"] for i in self.items)60 61 def get_total(self):62 subtotal = self._calculate_subtotal() #?usehelper63 return subtotal6465cart = ShoppingCart()66cart.add_item("Apple", 1.50, 3)output3 doubled twice = 12 === Helper Methods ===self.items ← []
39class ShoppingCart:40 def __init__(self⟨ShoppingCart C⟩):41 self.items→ [] = []cart ← ⟨ShoppingCart C⟩
65cart→ ⟨ShoppingCart C⟩ = ShoppingCart()66cart⟨ShoppingCart C⟩.add_item("Apple", 1.50, 3)67cart.add_item("Bread", 2.50)self.items ← [{'name': 'Apple', 'price': 1.5, 'qty': 3}]
pass 1 of 243def add_item(self⟨ShoppingCart C⟩, nameApple, price1.5, qty3=1):44 self.items→ [{'name': 'Apple', 'price': 1.5, 'qty': 3}].append({"name": nameApple, "price": price1.5, "qty": qty3})45 self._log_action(f"Added {qty3}x {nameApple}") #?privatehelperdef _log_action(self, message): #?helpermethod
pass 1 of 344 self.items.append({"name": name, "price": price, "qty": qty})45 self._log_action(f"Added {qty3}x {nameApple}") #?privatehelper4647def remove_item(self, name):48 for item in self.items:49 if item["name"] == name:50 self.items.remove(item)51 self._log_action(f"Removed {name}")52 return53 self._log_action(f"Not found: {name}")5455def _log_action(self⟨ShoppingCart C⟩, messageAdded 3x Apple): #?helpermethod56 print(f"[Cart] {messageAdded 3x Apple}")output[Cart] Added 3x AppleAll 3 passes — pass 1 is the card above pass messageqtyname1 Added 3x Apple 3 Apple 2 Added 1x Bread 1 Bread 3 Not found: Milk — Milk cart.add_item("Apple", 1.50, 3)
65cart = ShoppingCart()66cart⟨ShoppingCart C⟩.add_item("Apple", 1.50, 3)67cart⟨ShoppingCart C⟩.add_item("Bread", 2.50)68cart.remove_item("Milk") # Not foundself.items ← [{'name': 'Apple', 'price': 1.5, 'qty': 3}, {'name': 'Bread', 'price': 2.5, 'qty': 1}]
pass 2 of 243def add_item(self⟨ShoppingCart C⟩, nameBread, price2.5, qty1=1):44 self.items→ [{'name': 'Apple', 'price': 1.5, 'qty': 3}, {'name': 'Bread', 'price': 2.5, 'qty': 1}].append({"name": nameBread, "price": price2.5, "qty": qty1})45 self._log_action(f"Added {qty1}x {nameBread}") #?privatehelpercart.add_item("Bread", 2.50)
66cart.add_item("Apple", 1.50, 3)67cart⟨ShoppingCart C⟩.add_item("Bread", 2.50)68cart⟨ShoppingCart C⟩.remove_item("Milk") # Not found69print(f"Total: ${cart.get_total():.2f}")def remove_item(self, name):
47def remove_item(self⟨ShoppingCart C⟩, nameMilk):48 for item in self.items:49 if item["name"] == name:for item in self.items:
pass 1 of 247def remove_item(self, name):48 for item{'name': 'Apple', 'price': 1.5, 'qty': 3} in self.items[{'name': 'Apple', 'price': 1.5, 'qty': 3}, {'name': 'Bread', 'price': 2.5, 'qty': 1}]:49 if item["name"] == name:50 self.items.remove(item)for item in self.items:
pass 2 of 247def remove_item(self, name):48 for item{'name': 'Bread', 'price': 2.5, 'qty': 1} in self.items[{'name': 'Apple', 'price': 1.5, 'qty': 3}, {'name': 'Bread', 'price': 2.5, 'qty': 1}]:49 if item["name"] == name:50 self.items.remove(item)self._log_action(f"Not found: {name}")
52 return53self._log_action(f"Not found: {nameMilk}")cart.remove_item("Milk") # Not found
67cart.add_item("Bread", 2.50)68cart⟨ShoppingCart C⟩.remove_item("Milk") # Not found69print(f"Total: ${cart⟨ShoppingCart C⟩.get_total():.2f}")def get_total(self):
61def get_total(self⟨ShoppingCart C⟩):62 subtotal = self._calculate_subtotal() #?usehelper63 return subtotaldef _calculate_subtotal(self): #?calchelper
58def _calculate_subtotal(self⟨ShoppingCart C⟩): #?calchelper59 return sum(i["price"](empty) * i["qty"](empty) for i in self.items[{'name': 'Apple', 'price': 1.5, 'qty': 3}, {'name': 'Bread', 'price': 2.5, 'qty': 1}])subtotal ← 7.0
61def get_total(self):62 subtotal→ 7.0 = self._calculate_subtotal() #?usehelper63 return subtotal7.0print(f"Total: ${cart.get_total():.2f}")
68cart.remove_item("Milk") # Not found69print(f"Total: ${cart⟨ShoppingCart C⟩.get_total():.2f}")7071print("\n=== Validation Methods ===")7273class User:74 def __init__(self, username, email, age):75 self.username = username76 self.email = email77 self.age = age78 79 def _is_valid_username(self): #?validatemethod80 return len(self.username) >= 381 82 def _is_valid_email(self):83 return "@" in self.email and "." in self.email84 85 def _is_valid_age(self):86 return 0 <= self.age <= 15087 88 def is_valid(self): #?combinedvalid89 return (self._is_valid_username() and #?callvalidators90 self._is_valid_email() and 91 self._is_valid_age())92 93 def validation_errors(self): #?errorlist94 errors = []95 if not self._is_valid_username():96 errors.append("Username too short")97 if not self._is_valid_email():98 errors.append("Invalid email format")99 if not self._is_valid_age():100 errors.append("Invalid age")101 return errors102103user1 = User("alice", "alice@email.com", 25)104user2 = User("ab", "not-an-email", 200)outputTotal: $7.00 === Validation Methods ===self.username ← alice, self.email ← alice@email.com, self.age ← 25
pass 1 of 273class User:74 def __init__(self⟨User D⟩, usernamealice, emailalice@email.com, age25):75 self.username→ alice = usernamealice76 self.email→ alice@email.com = emailalice@email.com77 self.age→ 25 = age25user1 ← ⟨User D⟩
103user1→ ⟨User D⟩ = User("alice", "alice@email.com", 25)104user2 = User("ab", "not-an-email", 200)self.username ← ab, self.email ← not-an-email, self.age ← 200
pass 2 of 273class User:74 def __init__(self⟨User A⟩, usernameab, emailnot-an-email, age200):75 self.username→ ab = usernameab76 self.email→ not-an-email = emailnot-an-email77 self.age→ 200 = age200user2 ← ⟨User A⟩
103user1 = User("alice", "alice@email.com", 25)104user2→ ⟨User A⟩ = User("ab", "not-an-email", 200)105106print(f"User1 valid: {user1⟨User D⟩.is_valid()}")107print(f"User2 valid: {user2.is_valid()}")def is_valid(self): #?combinedvalid
pass 1 of 288def is_valid(self⟨User D⟩): #?combinedvalid89 return (self._is_valid_username() and #?callvalidators90 self._is_valid_email() and 91 self._is_valid_age())def _is_valid_username(self): #?validatemethod
pass 1 of 379def _is_valid_username(self⟨User D⟩): #?validatemethod80 return len(self.usernamealice) >= 3All 3 passes — pass 1 is the card above pass selfself.usernameself.emailself.ageerrors1 ⟨User D⟩ alice alice@email.com 25 — 2 ⟨User A⟩ ab — — — 3 ⟨User A⟩ ab not-an-email 200 [] → ['Username too short'] def _is_valid_email(self):
pass 1 of 282def _is_valid_email(self⟨User D⟩):83 return "@" in self.emailalice@email.com and "." in self.emaildef _is_valid_age(self):
pass 1 of 285def _is_valid_age(self⟨User D⟩):86 return 0 <= self.age25 <= 150print(f"User1 valid: {user1.is_valid()}")
106print(f"User1 valid: {user1⟨User D⟩.is_valid()}")107print(f"User2 valid: {user2⟨User A⟩.is_valid()}")108print(f"User2 errors: {user2.validation_errors()}")outputUser1 valid: Truedef is_valid(self): #?combinedvalid
pass 2 of 288def is_valid(self⟨User A⟩): #?combinedvalid89 return (self._is_valid_username() and #?callvalidators90 self._is_valid_email() and 91 self._is_valid_age())print(f"User2 valid: {user2.is_valid()}")
106print(f"User1 valid: {user1.is_valid()}")107print(f"User2 valid: {user2⟨User A⟩.is_valid()}")108print(f"User2 errors: {user2⟨User A⟩.validation_errors()}")outputUser2 valid: Falseerrors ← []
93def validation_errors(self⟨User A⟩): #?errorlist94 errors→ [] = []95 if not self._is_valid_username():errors ← ['Username too short']
94errors = []95if not self._is_valid_username():96 errors→ ['Username too short'].append("Username too short")97if not self._is_valid_email():def _is_valid_email(self):
pass 2 of 282def _is_valid_email(self⟨User A⟩):83 return "@" in self.emailnot-an-email and "." in self.emailerrors ← ['Username too short', 'Invalid email format']
96 errors.append("Username too short")97if not self._is_valid_email():98 errors→ ['Username too short', 'Invalid email format'].append("Invalid email format")99if not self._is_valid_age():def _is_valid_age(self):
pass 2 of 285def _is_valid_age(self⟨User A⟩):86 return 0 <= self.age200 <= 150errors ← ['Username too short', 'Invalid email format', 'Invalid age']
98 errors.append("Invalid email format")99if not self._is_valid_age():100 errors→ ['Username too short', 'Invalid email format', 'Invalid age'].append("Invalid age")101return errorsreturn errors
100 errors.append("Invalid age")101return errors['Username too short', 'Invalid email format', 'Invalid age']print(f"User2 errors: {user2.validation_errors()}")
107print(f"User2 valid: {user2.is_valid()}")108print(f"User2 errors: {user2⟨User A⟩.validation_errors()}")109110print("\n=== State Management ===")111112class Player:113 def __init__(self, name, health=100):114 self.name = name115 self.health = health116 self.alive = True117 118 def take_damage(self, amount):119 self.health -= amount120 print(f"{self.name} takes {amount} damage! Health: {self.health}")121 self._check_death() #?checkstate122 123 def heal(self, amount):124 if self.alive:125 self.health = min(100, self.health + amount)126 print(f"{self.name} heals {amount}! Health: {self.health}")127 128 def _check_death(self): #?statecheck129 if self.health <= 0 and self.alive:130 self.alive = False131 self._on_death() #?callback132 133 def _on_death(self): #?callbackmethod134 print(f"{self.name} has fallen!")135136hero = Player("Hero")137hero.take_damage(30)outputUser2 errors: ['Username too short', 'Invalid email format', 'Invalid age'] === State Management ===self.name ← Hero, self.health ← 100, self.alive ← True
112class Player:113 def __init__(self⟨Player E⟩, nameHero, health100=100):114 self.name→ Hero = nameHero115 self.health→ 100 = health100116 self.alive→ True = Truehero ← ⟨Player E⟩
136hero→ ⟨Player E⟩ = Player("Hero")137hero⟨Player E⟩.take_damage(30)138hero.take_damage(50)self.health ← 70
pass 1 of 3118def take_damage(self⟨Player E⟩, amount30):119 self.health→ 70 -= amount30120 print(f"{self.nameHero} takes {amount30} damage! Health: {self.health70}")121 self._check_death() #?checkstateoutputHero takes 30 damage! Health: 70All 3 passes — pass 1 is the card above pass amountself.healthself.alive1 30 100 → 70 — 2 50 70 → 20 — 3 30 20 → -10 True → False def _check_death(self): #?statecheck
pass 1 of 3120 print(f"{self.name} takes {amount} damage! Health: {self.health}")121 self._check_death() #?checkstate122123def heal(self, amount):124 if self.alive:125 self.health = min(100, self.health + amount)126 print(f"{self.name} heals {amount}! Health: {self.health}")127128def _check_death(self⟨Player E⟩): #?statecheck129 if self.health <= 0 and self.alive:130 self.alive = FalseAll 3 passes — pass 1 is the card above pass self.healthself.nameself.alive1 — — — 2 — — — 3 -10 Hero True → False hero.take_damage(30)
136hero = Player("Hero")137hero⟨Player E⟩.take_damage(30)138hero⟨Player E⟩.take_damage(50)139hero.take_damage(30) # Dieshero.take_damage(50)
137hero.take_damage(30)138hero⟨Player E⟩.take_damage(50)139hero⟨Player E⟩.take_damage(30) # Dies140hero.heal(20) # Can't heal when deadself.alive ← False
128def _check_death(self): #?statecheck129 if self.health-10 <= 0 and self.aliveTrue:130 self.alive→ False = False131 self._on_death() #?callbackdef _on_death(self): #?callbackmethod
120 print(f"{self.name} takes {amount} damage! Health: {self.health}")121 self._check_death() #?checkstate122123def heal(self, amount):124 if self.alive:125 self.health = min(100, self.health + amount)126 print(f"{self.name} heals {amount}! Health: {self.health}")127128def _check_death(self): #?statecheck129 if self.health <= 0 and self.alive:130 self.alive = False131 self._on_death() #?callback132133def _on_death(self⟨Player E⟩): #?callbackmethod134 print(f"{self.nameHero} has fallen!")outputHero has fallen!hero.take_damage(30) # Dies
138hero.take_damage(50)139hero⟨Player E⟩.take_damage(30) # Dies140hero⟨Player E⟩.heal(20) # Can't heal when deaddef heal(self, amount):
123def heal(self⟨Player E⟩, amount20):124 if self.alive:125 self.health = min(100, self.health + amount)hero.heal(20) # Can't heal when dead
139hero.take_damage(30) # Dies140hero⟨Player E⟩.heal(20) # Can't heal when dead141142print("\n=== Method Delegation ===")143144class Report:145 def __init__(self, title, data):146 self.title = title147 self.data = data148 149 def generate(self, format="text"): #?delegator150 if format == "text":151 return self._generate_text() #?delegate1152 elif format == "html":153 return self._generate_html() #?delegate2154 else:155 return self._generate_text()156 157 def _generate_text(self): #?textimpl158 lines = [f"=== {self.title} ==="]159 for key, value in self.data.items():160 lines.append(f"{key}: {value}")161 return "\n".join(lines)162 163 def _generate_html(self): #?htmlimpl164 html = [f"<h1>{self.title}</h1>", "<ul>"]165 for key, value in self.data.items():166 html.append(f" <li>{key}: {value}</li>")167 html.append("</ul>")168 return "\n".join(html)169170report = Report("Sales", {"Q1": 1000, "Q2": 1500, "Q3": 1200})171print(report.generate("text"))output === Method Delegation ===self.title ← Sales, self.data ← {'Q1': 1000, 'Q2': 1500, 'Q3': 1200}
144class Report:145 def __init__(self⟨Report F⟩, titleSales, data{'Q1': 1000, 'Q2': 1500, 'Q3': 1200}):146 self.title→ Sales = titleSales147 self.data→ {'Q1': 1000, 'Q2': 1500, 'Q3': 1200} = data{'Q1': 1000, 'Q2': 1500, 'Q3': 1200}report ← ⟨Report F⟩
170report→ ⟨Report F⟩ = Report("Sales", {"Q1": 1000, "Q2": 1500, "Q3": 1200})171print(report⟨Report F⟩.generate("text"))172print()def generate(self, format="text"): #?delegator
pass 1 of 2149def generate(self⟨Report F⟩, formattext="text"): #?delegator150 if format == "text":151 return self._generate_text() #?delegate1lines ← ['=== Sales ===']
157def _generate_text(self⟨Report F⟩): #?textimpl158 lines→ ['=== Sales ==='] = [f"=== {self.titleSales} ==="]159 for key, value in self.data.items():lines ← ['=== Sales ===', 'Q1: 1000']
pass 1 of 3158lines = [f"=== {self.title} ==="]159for keyQ1, value1000 in self.data{'Q1': 1000, 'Q2': 1500, 'Q3': 1200}.items():160 lines→ ['=== Sales ===', 'Q1: 1000'].append(f"{keyQ1}: {value1000}")161return "\n".join(lines)All 3 passes — pass 1 is the card above pass keyvaluelines1 Q1 1000 ['=== Sales ==='] → ['=== Sales ===', 'Q1: 1000'] 2 Q2 1500 ['=== Sales ===', 'Q1: 1000'] → ['=== Sales ===', 'Q1: 1000', 'Q2: 1500'] 3 Q3 1200 ['=== Sales ===', 'Q1: 1000', 'Q2: 1500'] → ['=== Sales ===', 'Q1: 1000', 'Q2: 1500', 'Q3: 1200'] return " ".join(lines)
160 lines.append(f"{key}: {value}")161return "\n".join(lines['=== Sales ===', 'Q1: 1000', 'Q2: 1500', 'Q3: 1200'])print(report.generate("text"))
170report = Report("Sales", {"Q1": 1000, "Q2": 1500, "Q3": 1200})171print(report⟨Report F⟩.generate("text"))172print()173print(report⟨Report F⟩.generate("html"))174#@help callselfoutput=== Sales === Q1: 1000 Q2: 1500 Q3: 1200def generate(self, format="text"): #?delegator
pass 2 of 2149def generate(self⟨Report F⟩, formathtml="text"): #?delegator150 if format == "text":151 return self._generate_text() #?delegate1html ← ['<h1>Sales</h1>', '<ul>']
163def _generate_html(self⟨Report F⟩): #?htmlimpl164 html→ ['<h1>Sales</h1>', '<ul>'] = [f"<h1>{self.titleSales}</h1>", "<ul>"]165 for key, value in self.data.items():html ← ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>']
pass 1 of 3164html = [f"<h1>{self.title}</h1>", "<ul>"]165for keyQ1, value1000 in self.data{'Q1': 1000, 'Q2': 1500, 'Q3': 1200}.items():166 html→ ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>'].append(f" <li>{keyQ1}: {value1000}</li>")167html.append("</ul>")All 3 passes — pass 1 is the card above pass keyvaluehtml1 Q1 1000 ['<h1>Sales</h1>', '<ul>'] → ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>'] 2 Q2 1500 ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>'] → ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>', ' <li>Q2: 1500</li>'] 3 Q3 1200 ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>', ' <li>Q2: 1500</li>'] → ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>', ' <li>Q2: 1500</li>', ' <li>Q3: 1200</li>'] html ← ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>', ' <li>Q2: 1500</li>', ' <li>Q3: 1200</li>', '</ul>']
166 html.append(f" <li>{key}: {value}</li>")167html→ ['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>', ' <li>Q2: 1500</li>', ' <li>Q3: 1200</li>', '</ul>'].append("</ul>")168return "\n".join(html['<h1>Sales</h1>', '<ul>', ' <li>Q1: 1000</li>', ' <li>Q2: 1500</li>', ' <li>Q3: 1200</li>', '</ul>'])print(report.generate("html"))
172print()173print(report⟨Report F⟩.generate("html"))174#@help callselfoutput<h1>Sales</h1> <ul> <li>Q1: 1000</li> <li>Q2: 1500</li> <li>Q3: 1200</li> </ul>
self.helper() calls the object's own method. Useful for code organization.
Common mistakes
Errors beginners make with self.
# Common self-Related Mistakes
print("=== Mistake 1: Forgetting self Parameter ===\n")
class WrongCalculator:
def __init__(self, value):
self.value = value
# WRONG: Missing self!
# def add(n):
# self.value += n # NameError: self not defined!
# CORRECT:
def add(self, n):
self.value += n
calc = WrongCalculator(10)
calc.add(5)
print(f"Value: {calc.value}")
print("\n=== Mistake 2: Forgetting self. for Attributes ===")
class WrongPerson:
def __init__(self, name):
self.name = name
def greet_wrong(self):
# WRONG: name without self.
# print(f"Hello, I'm {name}") # NameError: name not defined
pass
def greet_correct(self):
# CORRECT:
print(f"Hello, I'm {self.name}")
person = WrongPerson("Alice")
person.greet_correct()
print("\n=== Mistake 3: Using self Outside Methods ===")
class WrongCounter:
# WRONG: Can't use self here!
# count = self.something # NameError!
# Class attributes don't use self
count = 0
def __init__(self):
# Instance attributes DO use self
self.value = 0
print("Class attribute (no self):", WrongCounter.count)
print("\n=== Mistake 4: Calling Method Without self. ===")
class WrongHelper:
def __init__(self):
self.data = []
def process(self, item):
# WRONG: Calling without self.
# validate(item) # NameError: validate not defined
# CORRECT:
self._validate(item)
self.data.append(item)
def _validate(self, item):
if item is None:
raise ValueError("Item cannot be None")
helper = WrongHelper()
helper.process("test")
print(f"Data: {helper.data}")
print("\n=== Mistake 5: Confusing self and cls ===")
class ConfusingExample:
class_data = []
def __init__(self, value):
self.value = value
# Instance method uses self
def add_to_instance(self, item):
# self refers to THIS object
pass
# Class method uses cls
@classmethod
def add_to_class(cls, item):
# cls refers to the CLASS
cls.class_data.append(item)
# Static method uses neither
@staticmethod
def utility_function(x):
return x * 2
ConfusingExample.add_to_class("item1")
print(f"Class data: {ConfusingExample.class_data}")
print(f"Static result: {ConfusingExample.utility_function(5)}")
print("\n=== Mistake 6: Shadowing with Local Variable ===")
class WrongAssignment:
def __init__(self, name):
self.name = name
def change_name_wrong(self, new_name):
# WRONG: Creates local variable, not instance attr
name = new_name # This is a LOCAL variable!
print(f"Inside method: name = {name}")
print(f"self.name unchanged: {self.name}")
def change_name_correct(self, new_name):
# CORRECT:
self.name = new_name
print(f"self.name changed: {self.name}")
obj = WrongAssignment("original")
print(f"Before: {obj.name}")
obj.change_name_wrong("attempted")
print(f"After wrong: {obj.name}") # Still "original"
obj.change_name_correct("successful")
print(f"After correct: {obj.name}") # Changed!
print("\n=== Mistake 7: Returning Wrong Thing ===")
class WrongReturn:
def __init__(self, items):
self._items = items
def get_items_wrong(self):
# WRONG: Returns the internal list directly
return self._items # Caller can modify our data!
def get_items_correct(self):
# CORRECT: Return a copy
return self._items.copy()
obj = WrongReturn([1, 2, 3])
wrong_items = obj.get_items_wrong()
wrong_items.append(999) # Modifies original!
print(f"After wrong get: {obj._items}") # Has 999!
obj2 = WrongReturn([1, 2, 3])
correct_items = obj2.get_items_correct()
correct_items.append(999) # Only modifies copy
print(f"After correct get: {obj2._items}") # No 999
print("\n=== Summary of Common Mistakes ===")
print("""
1. Missing 'self' in method parameter
WRONG: def method(x):
RIGHT: def method(self, x):
2. Missing 'self.' when accessing attributes
WRONG: print(name)
RIGHT: print(self.name)
3. Using 'self' outside methods
WRONG: class X: data = self.something
RIGHT: class X: data = 0 # Class attr, no self
4. Calling methods without 'self.'
WRONG: helper_method()
RIGHT: self.helper_method()
5. Creating local variable instead of attribute
WRONG: name = value # Local variable
RIGHT: self.name = value # Instance attribute
""")
print("=== Mistake 1: Forgetting self Parameter === ")
3print("=== Mistake 1: Forgetting self Parameter ===\n")45class WrongCalculator:6 def __init__(self, value):7 self.value = value8 9 # WRONG: Missing self! #?missingself10 # def add(n):11 # self.value += n # NameError: self not defined!12 13 # CORRECT: #?correctself14 def add(self, n):15 self.value += n1617calc = WrongCalculator(10)18calc.add(5)output=== Mistake 1: Forgetting self Parameter ===self.value ← 10
5class WrongCalculator:6 def __init__(self⟨WrongCalculator A⟩, value10):7 self.value→ 10 = value10calc ← ⟨WrongCalculator A⟩
17calc→ ⟨WrongCalculator A⟩ = WrongCalculator(10)18calc⟨WrongCalculator A⟩.add(5)19print(f"Value: {calc.value}")self.value ← 15
13# CORRECT: #?correctself14def add(self⟨WrongCalculator A⟩, n5):15 self.value→ 15 += n5calc.add(5)
17calc = WrongCalculator(10)18calc⟨WrongCalculator A⟩.add(5)19print(f"Value: {calc.value15}")2021print("\n=== Mistake 2: Forgetting self. for Attributes ===")2223class WrongPerson:24 def __init__(self, name):25 self.name = name26 27 def greet_wrong(self):28 # WRONG: name without self. #?missingselfdot29 # print(f"Hello, I'm {name}") # NameError: name not defined30 pass31 32 def greet_correct(self):33 # CORRECT: #?correctselfdot34 print(f"Hello, I'm {self.name}")3536person = WrongPerson("Alice")37person.greet_correct()outputValue: 15 === Mistake 2: Forgetting self. for Attributes ===self.name ← Alice
23class WrongPerson:24 def __init__(self⟨WrongPerson B⟩, nameAlice):25 self.name→ Alice = nameAliceperson ← ⟨WrongPerson B⟩
36person→ ⟨WrongPerson B⟩ = WrongPerson("Alice")37person⟨WrongPerson B⟩.greet_correct()def greet_correct(self): # CORRECT: #?correctselfdot
32def greet_correct(self⟨WrongPerson B⟩):33 # CORRECT: #?correctselfdot34 print(f"Hello, I'm {self.nameAlice}")outputHello, I'm Alicecount ← (empty)
36person = WrongPerson("Alice")37person⟨WrongPerson B⟩.greet_correct()3839print("\n=== Mistake 3: Using self Outside Methods ===")4041class WrongCounter:42 # WRONG: Can't use self here! #?selfoutsidemethod43 # count = self.something # NameError!44 45 # Class attributes don't use self #?classattrnose46 count→ (empty) = 047 48 def __init__(self):49 # Instance attributes DO use self #?instanceattrself50 self.value = 05152print("Class attribute (no self):", WrongCounter.count0)5354print("\n=== Mistake 4: Calling Method Without self. ===")5556class WrongHelper:57 def __init__(self):58 self.data = []59 60 def process(self, item):61 # WRONG: Calling without self. #?callingwithoutself62 # validate(item) # NameError: validate not defined63 64 # CORRECT: #?correctcall65 self._validate(item)66 self.data.append(item)67 68 def _validate(self, item):69 if item is None:70 raise ValueError("Item cannot be None")7172helper = WrongHelper()73helper.process("test")output === Mistake 3: Using self Outside Methods === Class attribute (no self): 0 === Mistake 4: Calling Method Without self. ===self.data ← []
56class WrongHelper:57 def __init__(self⟨WrongHelper C⟩):58 self.data→ [] = []helper ← ⟨WrongHelper C⟩
72helper→ ⟨WrongHelper C⟩ = WrongHelper()73helper⟨WrongHelper C⟩.process("test")74print(f"Data: {helper.data}")def process(self, item): # WRONG: Calling without self. #?cal…
60def process(self⟨WrongHelper C⟩, itemtest):61 # WRONG: Calling without self. #?callingwithoutself62 # validate(item) # NameError: validate not defined63 64 # CORRECT: #?correctcall65 self._validate(itemtest)66 self.data.append(item)self.data ← ['test']
64 # CORRECT: #?correctcall65 self._validate(itemtest)66 self.data→ ['test'].append(itemtest)6768def _validate(self⟨WrongHelper C⟩, itemtest):69 if item is None:70 raise ValueError("Item cannot be None")class_data ← (empty)
72helper = WrongHelper()73helper⟨WrongHelper C⟩.process("test")74print(f"Data: {helper.data['test']}")7576print("\n=== Mistake 5: Confusing self and cls ===")7778class ConfusingExample:79 class_data→ (empty) = [] #?classdata80 81 def __init__(self, value):82 self.value = value83 84 # Instance method uses self #?instancemethod85 def add_to_instance(self, item):86 # self refers to THIS object87 pass88 89 # Class method uses cls #?classmethodcls90 @classmethod91 def add_to_class(cls, item):92 # cls refers to the CLASS93 cls.class_data.append(item)94 95 # Static method uses neither #?staticmethod96 @staticmethod97 def utility_function(x):98 return x * 299100ConfusingExample<class '__main__.ConfusingExample'>.add_to_class("item1")101print(f"Class data: {ConfusingExample.class_data}")outputData: ['test'] === Mistake 5: Confusing self and cls ===cls.class_data ← ['item1']
90@classmethod91def add_to_class(cls<class '__main__.ConfusingExample'>, itemitem1):92 # cls refers to the CLASS93 cls.class_data→ ['item1'].append(itemitem1)ConfusingExample.add_to_class("item1")
100ConfusingExample<class '__main__.ConfusingExample'>.add_to_class("item1")101print(f"Class data: {ConfusingExample.class_data['item1']}")102print(f"Static result: {ConfusingExample<class '__main__.ConfusingExample'>.utility_function(5)}")outputClass data: ['item1']def utility_function(x):
96@staticmethod97def utility_function(x5):98 return x5 * 2print(f"Static result: {ConfusingExample.utility_function(5)}")
101print(f"Class data: {ConfusingExample.class_data}")102print(f"Static result: {ConfusingExample<class '__main__.ConfusingExample'>.utility_function(5)}")103104print("\n=== Mistake 6: Shadowing with Local Variable ===")105106class WrongAssignment:107 def __init__(self, name):108 self.name = name109 110 def change_name_wrong(self, new_name):111 # WRONG: Creates local variable, not instance attr #?localvar112 name = new_name # This is a LOCAL variable!113 print(f"Inside method: name = {name}")114 print(f"self.name unchanged: {self.name}")115 116 def change_name_correct(self, new_name):117 # CORRECT: #?selfassign118 self.name = new_name119 print(f"self.name changed: {self.name}")120121obj = WrongAssignment("original")122print(f"Before: {obj.name}")outputStatic result: 10 === Mistake 6: Shadowing with Local Variable ===self.name ← original
106class WrongAssignment:107 def __init__(self⟨WrongAssignment D⟩, nameoriginal):108 self.name→ original = nameoriginalobj ← ⟨WrongAssignment D⟩
121obj→ ⟨WrongAssignment D⟩ = WrongAssignment("original")122print(f"Before: {obj.nameoriginal}")123obj⟨WrongAssignment D⟩.change_name_wrong("attempted")124print(f"After wrong: {obj.name}") # Still "original"outputBefore: originalname ← attempted
110def change_name_wrong(self⟨WrongAssignment D⟩, new_nameattempted):111 # WRONG: Creates local variable, not instance attr #?localvar112 name→ attempted = new_nameattempted # This is a LOCAL variable!113 print(f"Inside method: name = {nameattempted}")114 print(f"self.name unchanged: {self.nameoriginal}")outputInside method: name = attempted self.name unchanged: originalobj.change_name_wrong("attempted")
122print(f"Before: {obj.name}")123obj⟨WrongAssignment D⟩.change_name_wrong("attempted")124print(f"After wrong: {obj.nameoriginal}") # Still "original"125obj⟨WrongAssignment D⟩.change_name_correct("successful")126print(f"After correct: {obj.name}") # Changed!outputAfter wrong: originalself.name ← successful
116def change_name_correct(self⟨WrongAssignment D⟩, new_namesuccessful):117 # CORRECT: #?selfassign118 self.name→ successful = new_namesuccessful119 print(f"self.name changed: {self.namesuccessful}")outputself.name changed: successfulobj.change_name_correct("successful")
124print(f"After wrong: {obj.name}") # Still "original"125obj⟨WrongAssignment D⟩.change_name_correct("successful")126print(f"After correct: {obj.namesuccessful}") # Changed!127128print("\n=== Mistake 7: Returning Wrong Thing ===")129130class WrongReturn:131 def __init__(self, items):132 self._items = items133 134 def get_items_wrong(self):135 # WRONG: Returns the internal list directly #?returninternal136 return self._items # Caller can modify our data!137 138 def get_items_correct(self):139 # CORRECT: Return a copy #?returncopy140 return self._items.copy()141142obj = WrongReturn([1, 2, 3])143wrong_items = obj.get_items_wrong()outputAfter correct: successful === Mistake 7: Returning Wrong Thing ===self._items ← [1, 2, 3]
pass 1 of 2130class WrongReturn:131 def __init__(self⟨WrongReturn E⟩, items[1, 2, 3]):132 self._items→ [1, 2, 3] = items[1, 2, 3]obj ← ⟨WrongReturn E⟩
142obj→ ⟨WrongReturn E⟩ = WrongReturn([1, 2, 3])143wrong_items = obj⟨WrongReturn E⟩.get_items_wrong()144wrong_items.append(999) # Modifies original!def get_items_wrong(self): # WRONG: Returns the internal list …
134def get_items_wrong(self⟨WrongReturn E⟩):135 # WRONG: Returns the internal list directly #?returninternal136 return self._items[1, 2, 3] # Caller can modify our data!wrong_items ← [1, 2, 3]
142obj = WrongReturn([1, 2, 3])143wrong_items→ [1, 2, 3] = obj⟨WrongReturn E⟩.get_items_wrong()144wrong_items→ [1, 2, 3, 999].append(999) # Modifies original!145print(f"After wrong get: {obj._items[1, 2, 3, 999]}") # Has 999!146147obj2 = WrongReturn([1, 2, 3])148correct_items = obj2.get_items_correct()outputAfter wrong get: [1, 2, 3, 999]self._items ← [1, 2, 3]
pass 2 of 2130class WrongReturn:131 def __init__(self⟨WrongReturn D⟩, items[1, 2, 3]):132 self._items→ [1, 2, 3] = items[1, 2, 3]obj2 ← ⟨WrongReturn D⟩
147obj2→ ⟨WrongReturn D⟩ = WrongReturn([1, 2, 3])148correct_items = obj2⟨WrongReturn D⟩.get_items_correct()149correct_items.append(999) # Only modifies copydef get_items_correct(self): # CORRECT: Return a copy #?retur…
138def get_items_correct(self⟨WrongReturn D⟩):139 # CORRECT: Return a copy #?returncopy140 return self._items[1, 2, 3].copy()correct_items ← [1, 2, 3]
147obj2 = WrongReturn([1, 2, 3])148correct_items→ [1, 2, 3] = obj2⟨WrongReturn D⟩.get_items_correct()149correct_items→ [1, 2, 3, 999].append(999) # Only modifies copy150print(f"After correct get: {obj2._items[1, 2, 3]}") # No 999151152print("\n=== Summary of Common Mistakes ===")153print("""1541. Missing 'self' in method parameter155 WRONG: def method(x):156 RIGHT: def method(self, x):1571582. Missing 'self.' when accessing attributes159 WRONG: print(name)160 RIGHT: print(self.name)1611623. Using 'self' outside methods163 WRONG: class X: data = self.something164 RIGHT: class X: data = 0 # Class attr, no self1651664. Calling methods without 'self.'167 WRONG: helper_method()168 RIGHT: self.helper_method()1691705. Creating local variable instead of attribute171 WRONG: name = value # Local variable172 RIGHT: self.name = value # Instance attribute173""")174#@help missingselfoutputAfter correct get: [1, 2, 3] === Summary of Common Mistakes === 1. Missing 'self' in method parameter WRONG: def method(x): RIGHT: def method(self, x): 2. Missing 'self.' when accessing attributes WRONG: print(name) RIGHT: print(self.name) 3. Using 'self' outside methods WRONG: class X: data = self.something RIGHT: class X: data = 0 # Class attr, no self 4. Calling methods without 'self.' WRONG: helper_method() RIGHT: self.helper_method() 5. Creating local variable instead of attribute WRONG: name = value # Local variable RIGHT: self.name = value # Instance attribute
Forgetting self in method signature. Forgetting self when accessing attributes.
Exercise: practical.py
Build a class demonstrating proper use of self