Codeskill

Learn to code, step by step

Idiomatic Python

Python patterns that experienced developers reach for daily. None of this is magic – it is the everyday stuff that matters once your projects get past the basics. It is mostly shorter, clearer ways to do things you already know how to do with loops and temporary variables.

List and dict comprehensions

A comprehension builds a new list or dictionary in one expression. They read well once you are used to them, and they are often faster than appending in a loop.

names = ["alice", "bob", "cara"]
upper = [n.upper() for n in names]

scores = {"alice": 8, "bob": 3, "cara": 9}
passed = {name: score for name, score in scores.items() if score >= 5}

Keep comprehensions simple. If you need nested loops, multiple if clauses, and a ternary operator, a plain for loop is probably clearer.

Unpacking and the splat operators

Python lets you assign multiple values at once and spread iterables where a function expects separate arguments.

first, *middle, last = [1, 2, 3, 4, 5]
# first == 1, middle == [2, 3, 4], last == 5

def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}")

args = ("Alice",)
kwargs = {"greeting": "Hi"}
greet(*args, **kwargs)

Context managers with with

Files, database connections, and locks should be opened and closed predictably. A context manager handles setup and teardown even when an error occurs.

from pathlib import Path

path = Path("notes.txt")
with path.open("w", encoding="utf-8") as handle:
    handle.write("Remember the front sprocket.n")
# file is closed here, even if write() raised an error

You met with open(...) in the intro tutorials. The same idea applies anywhere you see with in library code.

enumerate and zip

Stop maintaining a manual counter when you need index and value together.

items = ["apple", "banana", "cherry"]
for index, item in enumerate(items, start=1):
    print(index, item)

names = ["Alice", "Bob"]
scores = [88, 92]
for name, score in zip(names, scores):
    print(name, score)

Truthiness and guard clauses

Empty strings, zero, empty lists, and None are falsy. Use that for early exits instead of deep nesting.

def slugify(text: str) -> str:
    if not text:
        return ""
    return text.strip().lower().replace(" ", "-")

f-strings and small formatting habits

f-strings are the default for readable string building. Format numbers when humans will read them.

total = 1234.5
print(f"Total: £{total:,.2f}")

user_id = 42
print(f"User {user_id!r} not found")  # !r calls repr() for debugging

Prefer pathlib over string paths

We go deeper on paths in tutorial 70. For now, get used to Path instead of concatenating strings with slashes.

from pathlib import Path

root = Path(__file__).resolve().parent
config = root / "config" / "settings.json"
if config.exists():
    print(config.read_text(encoding="utf-8"))

Idiomatic Python is mostly readability. If a colleague can scan your function and predict what it does without running it, you are on the right track. Next up: packaging your own module so other code (including your future projects) can import it cleanly.

PreviousGoing further with Python