Advanced Object-Oriented Programming
Advanced OOP is the heaviest PCPP1 domain and almost entirely code-reading: you get a short snippet - a diamond hierarchy, a property with no setter, a class defining __eq__ - and must state what Python does at runtime. Marks go to candidates who know the object model's real rules, not the textbook UML version.
Special methods
__str__is for users,__repr__for developers;print()falls back to__repr__when__str__is missing, never the reverse.- Defining
__eq__sets__hash__toNoneunless you define__hash__too, so instances silently become unhashable. functools.total_orderingfills in the remaining comparisons from__eq__plus one of__lt__,__le__,__gt__,__ge__.
Inheritance and super()
- C3 linearisation: with
Aas base of bothBandC,class D(B, C)gives the MROD → B → C → A → object. - Bare
super()moves to the next class in the MRO, not to "the parent" - that is what makes diamonds work.
Class-level tools
abc.ABCplus@abstractmethodraisesTypeErrorat instantiation, not when the subclass is defined.@x.settermust reuse the getter's name and be defined after it;@classmethodtakesclsand is the idiomatic alternative constructor;__slots__removes the instance__dict__.
Worked example
class A:
def ping(self): return 'A'
class B(A):
def ping(self): return 'B' + super().ping()
class C(A):
def ping(self): return 'C' + super().ping()
class D(B, C):
def ping(self): return 'D' + super().ping()
print(D().ping()) # 'DBCA'
Reasoning: each ping prepends its letter and delegates through super(), so control flows D→B→C→A and the string builds back outwards. If C.ping had called A.ping(self) directly, A would run twice and the diamond would break.
Traps
- Mutable class attributes are shared:
self.items.append(x)mutates the class-level list; only rebinding creates an instance attribute. isinstance(True, int)isTruebecauseboolsubclassesint;copy.copyshares nested objects,copy.deepcopydoes not.