Python: Do This, Not That! Logo

Basics

  • 1. Collection initialization
  • 2. Chained comparison operators
  • 3. Falsy and truthy
  • 4. Ternary operator
  • 5. Use any and all for boolean scans
  • 6. Use is None for None checks
  • 7. Use the walrus operator to avoid repeated work in conditions

Loops and Iteration

  • 1. Looping over a range of numbers
  • 2. Looping backward
  • 3. Looping over a collection
  • 4. Looping over a collection with indices
  • 5. Looping over two collections
  • 6. Use enumerate(start=…) for numbered loops
  • 7. Use next with a default for searches
  • 8. Use deque for queue operations
  • 9. Use heapq for top-N selection
  • 10. Combinations of a list
  • 11. Cycling through a list
  • 12. Product of lists

Lists and Comprehensions

  • 1. Use list comprehensions
  • 2. Filtering a list
  • 3. Count matching items without a temporary list
  • 4. Most frequent item in list
  • 5. Dictionary comprehension
  • 6. Set comprehension

Tuples and Strings

  • 1. Unpacking sequences and tuples
  • 2. Ignoring unpacked values from a tuple
  • 3. Avoid accessing tuple elements by index
  • 4. String concatenation
  • 5. String interpolation
  • 6. String debug
  • 7. Don’t repeat yourself (DRY)
  • 8. String reversal
  • 9. Use removeprefix and removesuffix

Functions and Context

  • 1. Clarify function calls with keyword arguments
  • 2. Lambdas
  • 3. Generator functions
  • 4. Use contextlib.suppress for narrow ignored exceptions
  • 5. Use nullcontext for optional context managers

Dictionaries and Data

  • 1. Default dictionary values: defaultdict
  • 2. Accessing a dictionary value with a default value
  • 3. Updating dictionaries
  • 4. Merging dictionaries
  • 5. Using a dictionary to store counts
  • 6. Transforming data with map, filter, reduce
  • 7. Flattening data
  • 8. Caching data and results

Files, Paths, and Exceptions

  • 1. Reading a file
  • 2. Deleting a file
  • 3. Filtering files
  • 4. Saving objects to file
  • 5. Prefer pathlib over os.path
  • 6. Re-raise exceptions deliberately

Classes and Misc

  • 1. Classes and dunders (double underscores)
  • 2. Enumerations
  • 3. Use default_factory for mutable dataclass defaults
  • 4. Simultaneous state updates
  • 5. Pandas apply and numpy vectorization

General Modern Python

  • 1. New vs old classes
  • 2. Use explicit namespaces with exec and eval

Python 3.9+

  • 1. Use functools.cache for unbounded memoization

Python 3.10+

  • 1. Use zip(strict=True) when equal lengths are required
  • 2. Use dataclass(slots=True) for simple records
  • 3. Use structural pattern matching for structural dispatch

Python 3.11+

  • 1. Use StrEnum for string enums
  • 2. Use Self for fluent instance return types
  • 3. Use tomllib for reading TOML
  • 4. Use asyncio.TaskGroup for related tasks
    • 4.1. Don’t do this
    • 4.2. Do this
  • 5. Use add_note for extra exception context
  • 6. Use except* for exception groups

Python 3.12+

  • 1. Use Path.walk in pathlib code
  • 2. Use explicit case sensitivity in pathlib globbing
  • 3. Use the type statement for type aliases
  • 4. Use @override for overridden methods
  • 5. Use itertools.batched for chunking

Python 3.13+

  • 1. Use copy.replace for shallow field updates
  • 2. Use Queue.shutdown instead of sentinel values

Python 3.14+

  • 1. Use InterpreterPoolExecutor for isolated parallel workers
  • 2. Use concurrent.interpreters instead of custom wrappers
  • 3. Use compression.zstd for standard-library Zstandard
  • 4. Use template strings for structured interpolation
Python: Do This, Not That!
  • 4. Use asyncio.TaskGroup for related tasks

4. Use asyncio.TaskGroup for related tasks

Use asyncio.TaskGroup instead of managing related tasks manually.

Task groups make the lifetime of related concurrent work explicit and give failures structured semantics instead of leaving cancellation and cleanup spread across the call site. That usually makes async code easier to reason about because the tasks that belong together are created, awaited, and torn down in one block.

Note

Python 3.11+

4.1. Don’t do this

1tasks = [
2    asyncio.create_task(fetch_user()),
3    asyncio.create_task(fetch_orders()),
4]
5user, orders = await asyncio.gather(*tasks)

4.2. Do this

1async with asyncio.TaskGroup() as tg:
2    user_task = tg.create_task(fetch_user())
3    orders_task = tg.create_task(fetch_orders())
4
5user = user_task.result()
6orders = orders_task.result()
Previous Next

© Copyright 2019, One-Off Coder. Last updated on Apr 10, 2026, 11:41:42 PM.