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

Python Basics in 8 Slides

Python · 12 slides
DAY 015 · POST 5 OF 5
(REMINDER)
DAY 015
Python Beginner Traps
@saurav_dnj_24github.com/SauravDnj · linkedin.com/in/sauravdnj
1 / 12

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 · Python Beginner Traps

This closing post is a field guide to the classic Python mistakes. Every one of these has bitten essentially every Python programmer at least once. The difference between a frustrated beginner and a confident one is often just having been burned by these already and learned the fix.

We'll walk five concrete traps with runnable demonstrations, plus the habits that prevent them as a category. Reading about a bug is far cheaper than spending three hours hunting it in your own code, so consider this post a tuition discount.

Slide 2 · 1. Mutable default args

The mutable default argument trap is the most infamous Python gotcha, and it's purely a consequence of when defaults are evaluated. The default value is computed once, at the moment the function is defined — not freshly on each call. So a default of [] creates a single list that every call without that argument shares.

The symptom is a function that mysteriously 'remembers' data across calls: the first call returns [1], the next returns [1, 2], and so on. It looks like spooky action, but it's just one shared list. The standard fix is the None sentinel: default to None and create a fresh list inside the function when the argument wasn't provided.

Slide 3 · The fix: None sentinel

This code demonstrates the bug and the fix side by side. In the buggy version, bucket=[] is that single shared list, so calling add(1) then add(2) accumulates into the same object — you see [1] then [1, 2] instead of the expected fresh list each time.

The fix is mechanical and worth memorizing as a reflex: never use a mutable default. Default to None, then inside the function check `if bucket is None: bucket = []`. This guarantees a fresh list on every call where the caller didn't supply one. Apply the same pattern to dicts and sets — any mutable default becomes None plus an inside-the-function construction.

Slide 4 · 2. Reference, not copy

The reference-versus-copy confusion follows directly from Python's value model covered in post three. `b = a` binds b to the very same list object a points at — there's still only one list. Mutating through either name is visible through both, because both are arrows to the same object.

When you genuinely want an independent copy, you must ask for one explicitly. For a flat list, a.copy() or list(a) gives a new top-level list. But beware: those are shallow copies — nested objects are still shared. For deeply nested data, reach for copy.deepcopy. Knowing which level of copying you need is the skill here.

Slide 5 · Copy vs alias

This snippet makes the alias trap concrete. After b = a, appending to b changes a too, because they're the same list — printing a shows the appended element. Then c = a.copy() creates a genuinely separate list, so appending to c leaves a untouched.

Run this and watch the two behaviors differ. The lesson: assignment never copies a mutable object, it only adds a name. Whenever you find two variables changing in lockstep when you expected independence, you've almost certainly got an alias where you wanted a copy. The fix is always an explicit .copy() or list().

Slide 6 · 3. == vs is

The == versus is distinction trips people up because both look like 'compare these.' But they ask different questions. == asks 'do these have the same value?' — it's what you want almost always. is asks 'are these literally the same object in memory?' — a much stronger and rarer condition.

The practical rule is simple: use == for comparing values, and reserve is for checking against None (and other singletons like True/False). `x is None` is the idiomatic, correct check. Using is to compare values like numbers or strings sometimes works by accident due to interning, then fails unpredictably — which is exactly why you shouldn't.

Slide 7 · 4. Off-by-one & ranges

Off-by-one errors and range pitfalls were introduced in post four, and they're worth restating among the classic traps because they're so persistent. range(n) stops before n, indexing starts at 0, and slice and loop right-bounds are exclusive. A length-3 list has indices 0, 1, 2, so lst[3] is out of bounds.

The mental fix is to fully embrace zero-based, half-open thinking. Once you stop expecting range(3) to include 3 and stop expecting index 3 to exist in a 3-element list, these errors simply vanish. The conventions are internally consistent — range(len(lst)) gives exactly the valid indices — so the adjustment is one-time, not perpetual.

Slide 8 · 5. Late binding in loops

Late binding in closures is a subtler trap, but it shows up the moment you build a list of functions in a loop. The lambdas don't capture the VALUE of i at creation time — they capture the variable i itself. By the time you call them, the loop has finished and i holds its final value, so every function returns that same final value.

The fix exploits the default-argument evaluation rule we just learned, turned to good use here: `lambda i=i: i` binds the current value of i to a default parameter at definition time, freezing it. Now each function carries its own captured value. It's the same mechanism behind the mutable-default trap, deployed deliberately.

Slide 9 · Debugging decision

This decision tree is a quick triage for the most common bugs. If two names changed together when you expected one to be independent, you have an alias — fix it with an explicit copy. If you're comparing something to None and getting odd results, switch from == to is. Otherwise, when an index or loop misbehaves, suspect your range and bounds.

Keep a triage like this in mind while debugging. Most beginner bugs fall into a small number of recognizable shapes, and learning to pattern-match the symptom to the cause is what makes debugging fast instead of frustrating. The tree turns a vague 'something's wrong' into a directed search.

Slide 10 · Habits that prevent all of it

These habits prevent the traps as categories rather than one at a time. Default arguments should always be None for anything mutable. Copy explicitly whenever you need independence. Use is only for None. Internalize zero-based, half-open counting. And bind loop variables with default arguments when building closures.

Notice how several of these trace back to the same root cause — Python's reference semantics and the one-time evaluation of defaults. Understanding those two mechanisms deeply, as we did in post three, means you don't have to memorize each trap separately; you can derive the fix from first principles every time.

Slide 11 · Silencing instead of reading

The final and most important meta-mistake is silencing errors instead of reading them. The instinct to wrap failing code in try/except and move on is understandable but corrosive — you're throwing away the most precise diagnostic information you'll ever get. A traceback names the exact error type and the exact line.

Learn to read tracebacks bottom-up: the last line tells you what went wrong, and the lines above show the call path that led there. Treat the error message as a helpful collaborator pointing at the problem, not an obstacle to suppress. This single attitude shift, more than any specific fix, is what turns hours of flailing into minutes of targeted repair.

Slide 12 · Save this. Follow for Day 16.

That closes Day 15 and your tour of Python basics. Across five posts you've covered the concepts, the motivation, the mechanics, the runnable code, and the traps. You now have both the vocabulary and the judgment to write and debug real Python.

Day 16 builds directly on this foundation. With the fundamentals solid, you're ready to level up — keep practicing the snippets from post four, internalize the traps from this one, and you'll find the rest of the AI series clicks into place far more easily.

🎨 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.