Pythonic Power Tools
Advanced · 20 min read · ▶ live playground · ✦ checkpoint
Python has small tools that make code shorter without making it clever for its own sake. Pythonic means clear Python that uses patterns Python programmers expect, not code that shows off.
Name a value inside a condition
The walrus operator is :=, an operator that assigns a name inside an expression. Its formal name is assignment expression, which means an assignment that also produces the assigned value.
Use it when it removes repeated work and keeps the condition readable. Think of it as writing a name tag at the same moment you check the value.
lines = ["Mina,90", "", "Ravi,73", "Noor,105"]
for line in lines:
if (clean := line.strip()):
player, score_text = clean.split(",")
score = int(score_text)
if 0 <= score <= 100:
print(f"{player}: saved {score}")
else:
print(f"{player}: invalid score")Mina: saved 90Ravi: saved 73Noor: invalid score
clean := line.strip() strips the line, stores the result in clean, and lets the if test that same result for truthiness. The blank line becomes "", so the body is skipped.
The score check uses a chained comparison, which is one comparison written as a clean range test. 0 <= score <= 100 reads like math and means the same as 0 <= score and score <= 100, but Python only evaluates the middle expression once. Picture it as one range gate: the score must pass the lower rail and the upper rail.
Unpack more than one shape
Advanced unpacking means using * and ** to gather or spread multiple values at once. You have used unpacking before; this is the power-tool version.
Reuse the list-row picture: unpacking pulls cards from a row into name tags. A starred name gathers the leftover cards into a list.
roster = ["Mina", "Ravi", "Noor", "Ada"]
captain, *middle_players, substitute = roster
print(captain)
print(middle_players)
print(substitute)
base_profile = {"player": "Mina", "mood": "ready", "points": 80}
latest_profile = {"points": 95, "badge": "silver"}
profile = {**base_profile, **latest_profile}
print(profile)
def label_player(player, points, mood="ready"):
return f"{player}: {points} points, {mood}"
args = ("Noor", 91)
kwargs = {"mood": "focused"}
print(label_player(*args, **kwargs))Mina['Ravi', 'Noor']Ada{'player': 'Mina', 'mood': 'ready', 'points': 95, 'badge': 'silver'}Noor: 91 points, focused
*middle_players gathers the values between the first and last item. {**base_profile, **latest_profile} builds a new dictionary; when keys repeat, the later dictionary wins. At the call site, *args spreads positional arguments, and **kwargs spreads keyword arguments.
Copy nested data carefully
A shallow copy copies the outer collection but still shares nested mutable objects inside it. You saw this with list copies. A deep copy recursively copies nested containers and objects as needed so nested mutable data can be independent; immutable objects may be reused.
Use the list-row model from earlier: a shallow copy makes a new outer row, but a nested row inside it can still be the same row. A deep copy can rebuild nested rows so mutable data is separate.
from copy import copy, deepcopy
practice_plan = {
"Mina": ["stretch", "puzzle"],
"Ravi": ["run"],
}
shallow_plan = copy(practice_plan)
deep_plan = deepcopy(practice_plan)
shallow_plan["Mina"].append("review")
deep_plan["Ravi"].append("breathe")
print("original Mina:", practice_plan["Mina"])
print("original Ravi:", practice_plan["Ravi"])
print("deep Ravi:", deep_plan["Ravi"])original Mina: ['stretch', 'puzzle', 'review']original Ravi: ['run']deep Ravi: ['run', 'breathe']
The shallow copy made a new outer dictionary, but the list for "Mina" was still the same list object in both dictionaries. The deep copy gave "Ravi" a separate nested list.
Try it - choose the clear condition
Run this, then change one score to 101 and one name to an empty string. Keep the version that you would rather read next month.
Reach for collections
collections is a standard-library module with specialized container types. You met it in the standard-library tour; now use the tools for real choices, not just names.
Use these when the name matches your problem:
Counteris a dictionary-like tally tool for counting items, merging counts, and asking for the most common values.defaultdictis a dictionary that creates a default value for missing keys, which is great for grouping.dequeis a fast double-ended queue, a collection built for adding and removing at both ends.namedtupleis a tuple type with named fields for fixed-shape records that should stay immutable.
from collections import Counter, defaultdict, deque, namedtuple
snacks = ["apples", "tea", "apples", "mango", "tea", "apples"]
tally = Counter(snacks)
tally.update(["tea", "tea"])
print(tally.most_common(2))
print(tally["bananas"])
players_by_mood = defaultdict(list)
for player, mood in [("Mina", "focused"), ("Ravi", "ready"), ("Noor", "focused")]:
players_by_mood[mood].append(player)
print(dict(players_by_mood))
print(players_by_mood["resting"])
line = deque(["Ravi", "Noor"])
line.appendleft("Mina")
line.append("Ada")
print(line.popleft())
recent_tasks = deque(maxlen=3)
for task in ["stretch", "run", "puzzle", "review"]:
recent_tasks.append(task)
print(list(recent_tasks))
Score = namedtuple("Score", ["player", "points"])
result = Score("Noor", 91)
better = result._replace(points=96)
print(result)
print(better)[('tea', 4), ('apples', 3)]0{'focused': ['Mina', 'Noor'], 'ready': ['Ravi']}[]Mina['run', 'puzzle', 'review']Score(player='Noor', points=91)Score(player='Noor', points=96)
Counter returns 0 for a missing item instead of raising KeyError, which is perfect for counts. defaultdict(list) makes grouping code clean, but reading players_by_mood["resting"] creates that missing key; use get() when you only want to check. deque(maxlen=3) keeps a fixed-size recent history by dropping the oldest item. namedtuple gives you tuple behavior plus field names, and _replace() returns a new record instead of mutating the old one.
Plain dict, list, and tuple are still the default choices. Reach for collections when your code is already spelling out a known pattern: tallying, grouping, queueing, keeping recent history, or naming tuple fields.
Checkpoint
Answer all three to mark this lesson complete
These tools are sharp because they let you say common Python ideas directly. Next, the course moves into text and time, where regular expressions and date handling reward the same habit: clear code first, compact code only when it helps.