✎ Edit content·DAY 020 · POST 5 OF 5 · Common Mistakes

Classes & OOP in Python

Python · 13 slides
DAY 020 · POST 5 OF 5
(REMINDER)
DAY 020
OOP Mistakes
@saurav_dnj_24github.com/SauravDnj · linkedin.com/in/sauravdnj
1 / 13

Theme

Palette

Download

4K — sharpest, slowest
🎬 Video options
Preparing preview…
Live preview · loops the “none” effect
All rendering runs in your browser. No server, no cost, no upload. MP4/WebM = full motion + effects · GIF = lightweight loop · PNG/PDF = static for the Instagram & LinkedIn carousel.

Caption (tap to copy)

📖 Deep dive (full written explanation)

The slides stay clean and scannable. Here's the in-depth explanation behind each one — great for the blog version, show notes, or studying the topic properly.
Slide 1 · OOP Mistakes

This cover frames the final angle as a field guide to failure modes. The hook makes an important point: the very flexibility that lets classes model anything is also what makes them dangerous when misused. Sharp tools cut both ways.

The emphasis on 'most OOP bugs aren't syntax errors' is deliberate. Beginners expect mistakes to announce themselves with a traceback, but the worst class problems are silent or structural: shared state that leaks between objects, equality that quietly lies, and inheritance tangles that pass review but rot over time. Learning to anticipate these is what separates a beginner's class from code that survives a growing codebase.

Slide 2 · 1. Mutable class attribute, shared by all

The first and most common mistake is the mutable class attribute, and it follows directly from the lookup order taught in the how post. Putting a list or dict in the class body creates ONE object that every instance shares through the instance-then-class fallback. It looks like per-object state but isn't — appending to one object's list changes what every object sees.

The fix is consistent and worth internalizing as a rule: per-instance mutable state must be created inside __init__ on self, never declared in the class body. The class body is for genuinely shared constants; anything mutable that should differ per object belongs on self. This single discipline prevents an entire family of mysterious shared-state bugs.

Slide 3 · The shared-state bug and fix

This code slide makes the shared-state bug undeniable. Cart declares items = [] in the class body, so when one cart adds an apple, a completely separate cart suddenly reports that same apple — the list leaked across instances because there's only one of it. The fix below moves the list into __init__ so each cart gets its own.

The danger is that the bug is invisible in trivial testing with a single object and only surfaces when two objects exist and one mysteriously sees the other's data. Running both versions and watching the second cart's items go from leaked to empty is the fastest way to feel why the location of the assignment — class body versus __init__ — changes everything.

Slide 4 · 2. Forgetting self

The second mistake covers the family of errors around forgetting self. Omit self from a method signature and Python still passes the instance into the first slot, raising a TypeError complaining about argument counts. Reference an attribute as a bare name like balance instead of self.balance and you get a NameError or accidentally read an unrelated variable.

self is mandatory because it's the only way a method reaches the object it belongs to — it's the mechanism the how post showed being passed automatically. There's no implicit 'this' in Python as in some other languages; you must name it explicitly. Once you internalize that every method needs self and every instance attribute needs the self. prefix, this entire category of errors disappears.

Slide 5 · self is not optional

This code slide demonstrates self in action and points at the failure. The working Counter declares self in bump() and accesses self.n, so the call returns the incremented value. The comment shows what breaks: drop self from the signature and Python raises a TypeError because it still tries to pass the instance into a method that has no slot for it.

The diagnostic value is direct. A 'takes 0 positional arguments but 1 was given' error on a method call almost always means a missing self in the definition. Recognizing that error message as the signature of this specific mistake turns a confusing traceback into an instant fix.

Slide 6 · 3. Mutable default argument

The third mistake is the mutable default argument, one of Python's most notorious gotchas, and it appears constantly in __init__. A default like items=[] is evaluated exactly once, when the function is defined — not on each call. So every call that omits the argument shares that same single list, and two supposedly independent objects end up with shared state.

The fix is the None-sentinel pattern: default the parameter to None, then inside the body create a fresh list when the argument wasn't provided. This guarantees a new list on every call. The pattern is so common that recognizing items=None followed by 'if items is not None else []' as idiomatic Python is itself a marker of experience.

Slide 7 · The None-sentinel fix

This code slide shows the bug and its standard remedy side by side. The buggy Bag defaults items to [], so the list is created once at definition time and reused across every Bag built without an explicit list — they all share it. The fixed Bag defaults to None and creates a fresh list inside __init__ whenever none was passed.

The None-sentinel idiom looks slightly more verbose but is precise and correct. The conditional 'items if items is not None else []' runs on every call, guaranteeing each object gets its own list while still allowing a caller to pass in a specific one. This is the canonical Python fix for mutable defaults, applicable far beyond classes.

Slide 8 · 4. Inheritance where composition fits

The fourth mistake is structural rather than mechanical: reaching for inheritance when composition is the better fit. Deep inheritance trees are brittle — a change in a base class ripples unpredictably through every subclass, and the 'is-a' relationship inheritance implies gets forced onto relationships that are really 'has-a.' A Car is not a kind of Engine; it HAS an engine.

Composition — holding another object as an attribute and delegating to it — is looser and more flexible. You can swap the held object, test it in isolation, and avoid the tight coupling a base class imposes. The guideline 'favor composition over inheritance' is one of the most repeated pieces of OOP wisdom precisely because beginners reach for inheritance reflexively whenever they want to reuse code.

Slide 9 · is-a vs has-a

The comparison clarifies when each relationship genuinely fits. Inheritance models 'is-a': a Dog is an Animal, so inheriting Animal's behavior makes sense — but it couples the subclass tightly to the base and grows brittle as the hierarchy deepens. Composition models 'has-a': a Car has an Engine, so holding an Engine object and delegating to it fits the real relationship and keeps the pieces loosely coupled and swappable.

The test to apply is the sentence itself. If 'X is a Y' is true and stable, inheritance may fit. If the honest relationship is 'X has a Y' or 'X uses a Y,' composition is right. Forcing a 'has-a' relationship into inheritance just to reuse code is the specific error this slide warns against.

Slide 10 · 5. The god-class

The fifth mistake is the god-class — a single class that accumulates half the program's state and dozens of methods until it's a procedural blob wearing a class's clothing. It violates the spirit of OOP, which is about small objects each owning one well-defined responsibility, not one object owning everything.

The consequences are concrete: a god-class is hard to test because everything is entangled, impossible to reuse because nothing is separable, and fragile because any change risks breaking unrelated behavior buried in the same class. The remedy is the single-responsibility idea — split the class so each piece does one thing and knows only what it needs. Recognizing a class that's grown too large is a prompt to decompose it, not to keep adding methods.

Slide 11 · 6. == breaks without __eq__

This code slide returns to a mechanical bug to close the post, demonstrating why == breaks without __eq__. Two Point objects with identical coordinates compare as False by default, because Python falls back to identity comparison — are these the same object in memory? — rather than value comparison. The fix defines __eq__ to compare the coordinates, after which equal points compare equal.

This is a silent correctness bug exactly like the others: nothing errors, the comparison just returns the wrong answer, which can quietly break lookups, deduplication, and tests. It ties back to the dunder methods from the code post — value equality is something you must opt into by defining __eq__ (or by using @dataclass, which generates it). The lesson is to define __eq__ whenever value-equality is what you mean.

Slide 12 · Stay out of trouble

The tips slide consolidates all the mistakes into a defensive checklist: put per-object state in __init__ on self, never default an argument to a list or dict, always declare self first, favor composition over deep inheritance, and define __eq__ when value-equality matters. Each bullet maps to one failure mode from the post.

Designed as a final reference, this card lets a reader audit any class they write against the known traps. Together with the patterns card from the code post, it forms a complete practical toolkit — the patterns tell you what to write, and these tips tell you what to avoid.

Slide 13 · Save this. Follow for Day 21.

This CTA closes the mistakes post and the full five-post arc on classes and OOP: concept, motivation, mechanics, patterns, and pitfalls. The reader now has a complete picture of what a class is, why it earns its place, how Python implements it, how to write one, and what goes wrong.

The teaser points forward to the next entry in the series, keeping the momentum of the 100 Days of AI sequence while signaling that classes are now a tool the reader can deploy confidently as the Python foundation continues to build toward the AI content ahead.

🎨 AI image prompt (matches this theme + palette)

Paste into Midjourney, DALL·E, Ideogram, etc. to generate an on-brand image, then upload it on the Edit content page. The prompt updates automatically with the selected theme + palette.