POST 1 of 5 MorningPythonConcept
Composition over inheritance — read this once
If there's one piece of OO advice that's universally right and universally ignored, it's this: prefer composition over inheritance. Most code that breaks under inheritance breaks because someone reached for inheritance when composition would have been simpler. The symptom — class hierarchies more than two levels deep. Animal → Mammal → Dog → GoldenRetriever. By level three, you're tying changes at level zero to behaviour at level three, and small modifications cascade in ways you didn't predict. The fix — favour 'has-a' over 'is-a'. Old instinct: 'A WeightedSampler is-a Sampler', so make WeightedSampler extend Sampler. New instinct: 'A WeightedSampler has-a Sampler under the hood', so pass the sampler in via __init__ and call it. The difference is subtle in code, profound in consequences. Composition keeps your inheritance tree flat. Each class is responsible for its own state and delegates other concerns to collaborators it received in the constructor. Testing becomes easy — pass a fake collaborator. Refactoring becomes easy — swap one collaborator without touching the others. When IS inheritance the right tool? Framework hooks. PyTorch's nn.Module, Django's models.Model, Flask's views — these are designed to be subclassed. The framework expects it; the subclass slot is part of the contract. Tiny mixins for cross-cutting concerns. A SerializableMixin that adds a to_json method. A LoggableMixin that adds debug logging. Duck typing via Protocols. If you just want polymorphism (different objects responding to the same method), Python's protocols (PEP-544) give you that without requiring inheritance at all. For your own business logic, default to composition. The day you find yourself debugging multiple inheritance with diamond patterns is the day you wish you had.
#Python#AI#100DaysOfCode#BuildInPublic#PythonProgramming#OOP