10 Hidden Python Tricks Every Developer Should Know in 2025
These aren’t party tricks; they’re the small, sharp tools that make production Python calmer and faster. Each pattern below includes a tiny example and a reason you’ll actually use it.
1) Assignment Expressions (:=) — trim repetition, clarify intent
The “walrus” lets you combine evaluation and assignment in a single expression.
# before
line = input()
if len(line) > 10:
print(len(line))
# after
if (n := len(line)) > 10:
print(n)
Use when: a value is computed once but needed in multiple checks or branches.
2) Structural Pattern Matching (PEP 634) — readable branching
Pattern matching makes complex conditionals expressive, especially for parsing or protocol handling.
def handle(msg):
match msg:
case {"type": "ping"}:
return {"type": "pong"}
case {"type": "sum", "nums": [a, b]}:
return {"type": "result", "value": a + b}
case _:
return {"error": "unknown"}
Use when: decoding JSON payloads, AST nodes, or command messages.
3) contextlib power: ExitStack & suppress — fewer try/finally ladders
Manage multiple context managers dynamically; silence known-safe exceptions.
from contextlib import ExitStack, suppress
with ExitStack() as stack:
files = [stack.enter_context(open(p, "r")) for p in ("a.txt","b.txt")]
with suppress(FileNotFoundError):
optional = open("maybe.txt").read()
Use when: opening N resources conditionally, composing contexts at runtime.
4) itertools & generators — pipelines that don’t blow RAM
from itertools import islice
def lines(path):
with open(path) as f:
for line in f:
yield line.strip()
for row in islice(lines("big.csv"), 0, 10_000):
...
Use when: streaming large files, building lazy transforms.
5) pathlib done right — no more brittle string paths
from pathlib import Path
root = Path("/var/app")
for p in root.glob("**/*.log"):
if p.stat().st_size > 1_000_000:
p.unlink(missing_ok=True)
Use when: you want portable joins, globs, and safe deletes.
6) zoneinfo & aware datetimes — time math without pain
from datetime import datetime
from zoneinfo import ZoneInfo
now_berlin = datetime.now(ZoneInfo("Europe/Berlin"))
expiry = now_berlin.replace(hour=23, minute=59)
Use when: scheduling, logs, and cross-region services.
7) dataclasses vs Pydantic v2 — speed vs validation
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
# Pydantic v2 for validation / serialization
from pydantic import BaseModel
class User(BaseModel):
id: int
email: str
Rule of thumb: dataclasses for plain data and speed; Pydantic when inputs can be messy.
8) async comprehensions & task groups — concurrent elegance
import asyncio
from asyncio import TaskGroup
async def fetch(i): ...
async def main():
async with TaskGroup() as tg:
results = [tg.create_task(fetch(i)) for i in range(100)]
asyncio.run(main())
Use when: you fan out I/O concurrently and need robust cancellation/propagation.
9) functools.cache / lru_cache — free wins for pure functions
from functools import cache
@cache
def fib(n:int) -> int:
return n if n < 2 else fib(n-1) + fib(n-2)
Use when: repeated calls with identical inputs (pure, deterministic).
10) PEP 621 packaging snippet — one file to rule builds
[project]
name = "toolkit"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = ["pydantic>=2", "httpx"]
[project.scripts]
toolkit = "toolkit.cli:app"
Use when: you want clean installs, console scripts, and CI-friendly releases.
Bonus: a tiny quality checklist
- ruff / black clean
- Type hints on public functions
- Docstring + one example for each “trick”
- Micro-benchmarks only where it matters
“Clarity over cleverness. Flow over force.” — redesign.ir
Comments
Join the discussion. We keep comments private to your device until moderation tooling ships.