ICPO names Python’s attribute lookup order: instance, class, parent classes, and finally object. When evaluating a.b, Python returns the first matching attribute it finds along that path. section 9.10
Use the lookup sequence to reason about methods, inheritance, and shared class attributes:
- Instance: Python first checks the object itself, so an instance attribute can hide an attribute with the same name on its class. section 9.10 section 9.12
- Class: if the instance lacks the attribute, Python checks its class. Methods such as str.upper are found on the class and called through the instance. section 9.10
- Parents and object: if the class lacks the attribute, Python searches parent classes and ultimately the object hierarchy, making inherited behavior available without duplication. section 9.10 section 9.15
- Dynamic class selection: referring to self.max_scoops lets one method use Bowl.max_scoops for Bowl instances and BigBowl.max_scoops for BigBowl instances. section 9.17
Inherited methods and overridden lookup
For `class Foo` with `x2` and `class Bar(Foo)` with `x3`, `b = Bar(10)` makes `b.x2()` find the method on Foo and `b.x3()` find it on Bar. Similarly, `s.upper()` finds `upper` on str after failing to find it on the string instance. A class attribute such as `Bowl.max_scoops` remains available through an instance, but assigning `one_bowl.max_scoops` creates an instance attribute that blocks the class value. When a subclass initializer must preserve parent setup, `super().__init__(name)` calls the parent initializer without naming the parent explicitly. section 9.10 section 9.12 section 9.15
class Foo():
def __init__(self, x):
self.x = x
def x2(self):
return self.x * 2
class Bar(Foo):
def x3(self):
return self.x * 3
b = Bar(10)
print(b.x2()) #1
print(b.x3()) #2
Runnable Foo and Bar code makes instance, class, and parent lookup visible through calls to inherited and subclass-specific methods. section 9.10

