Learn Python with Talk Python's 270 hours of courses

Clean Code in Python

Episode #404, published Mon, Feb 20, 2023, recorded Sun, Feb 12, 2023

Clean code is one of those aspects of your programming career that's easy to put on the back burner (sometimes by management more than yourself). But it's important in the short term for writing more debuggable and readable code. And important in the long run for avoiding having your program take on the dreaded "legacy code" moniker. We're fortunate to have Bob Belderbos back on the show. He's been thinking and writing about clean code and Python a lot lately and we'll dive into a bunch of tips you can use right away to make your code cleaner.

Watch this episode on YouTube
Play on YouTube
Watch the live stream version

Episode Deep Dive

Guests Introduction and Background

Bob Belderbos is the co-founder of PyBites and a seasoned Python developer who focuses on clean code, refactoring, and mentorship. He left a corporate role at Oracle to pursue PyBites full time, supporting developers through challenges, articles, and coaching programs. Bob has authored numerous Python exercises, courses, and code-quality resources, making him an ideal guest to discuss how to write and maintain clean, readable, and maintainable Python code.

What to Know If You're New to Python

If you’re just starting out with Python and want to get the most from this clean-code conversation, here are a few key considerations:

  • Understand basic Python syntax (indentation, blocks, and the def, class, if statements).
  • Recognize why Python emphasizes readability—you’ll see it helps implement many of these clean code practices more naturally.
  • Learn about packages like black, flake8, and pytest—they are tools frequently mentioned in discussions on code quality.
  • Explore how exceptions work in Python so that you can follow best practices for narrower, more explicit try/except blocks.

Key Points and Takeaways

  1. Focus on Smaller, Single-Purpose Code Breaking your code into smaller, single-purpose functions and classes keeps it maintainable. This approach makes it easier to test, reuse, and spot bugs early rather than wading through massive functions that do multiple things. In the episode, Bob mentioned that this practice also reduces “WTF” moments for teams by making your code more predictable. Keep your functions short and your classes focused on a single responsibility.
  2. Naming Matters: Clarity in Naming Names in code serve as the first line of documentation. Using descriptive names for variables, functions, and constants is far better than relying on comments explaining ambiguous or overly short names. Good naming can reduce the need for extra documentation and help teams navigate and extend code more easily.
  3. Watch Out for Magic Numbers Magic numbers (e.g., 365, 5, 87) sprinkled throughout code can be confusing and error-prone. If a number (or other literal) has meaning beyond its face value, give it a name or store it in a named constant. This makes your code more readable and signals the intent behind particular values.
  4. Be Mindful of Global Scope Overusing global variables and shared state invites confusion and bugs—functions can have hidden side effects, and team members can’t easily discover which parts of the program will modify global state. Instead, pass needed data through function parameters. If you must create constants in the global namespace, consider designating them as uppercase constants or even adding typing.final to clarify they should not be changed.
  5. Refactoring Is an Ongoing Process Perfect code structure rarely emerges from the first draft. Bob emphasized that continuous refactoring is how you keep technical debt under control. This also relieves the pressure to “get it right” on the first attempt. Embrace the idea that updating existing code for clarity and correctness is a natural, necessary part of development.
  6. Utilize Linting & Formatting Tools Tools like flake8, black, mypy, and pre-commit hooks automate style enforcement, catching code smells before they reach production. A quick local setup of these tools ensures consistent code quality across teams and removes manual style debates (“single or double quotes?”). “If it’s not automated, it’s broken,” as Bob put it.
  7. Embrace Idiomatic Python Pythonic code capitalizes on language features like all(), any(), list/set comprehensions, with context managers, and even EAFP (“easier to ask forgiveness than permission”) for opening files or working with dictionaries. It also means using built-in data structures and the standard library before rolling your own solutions. Writing code that follows the “zen” of Python often leads to simpler, more efficient solutions.
  8. Narrow Your Exception Blocks Wrapping large code blocks in a broad try/except can hide unintended errors and produce unclear error-handling. By focusing your try blocks on exactly the lines likely to fail, you ensure that you catch only the relevant exceptions. Catching Exception blindly can create more problems than it solves.
  9. Use Guard Clauses to Flatten Code Deeply nested if-statements (the “arrow” or “staircase” shape) are hard to follow. Instead, handle error or edge cases early and return immediately if something is invalid. This keeps the “happy path” visible and reduces cyclomatic complexity, making your logic more straightforward to maintain.
  10. Choose the Right Data Structures Lists, sets, dictionaries, and decks (from collections) each serve different performance needs. Don’t rely solely on lists if you need fast lookups; a set or dict might be drastically faster. The right data structure ensures scalability and improves clarity by reflecting the problem domain more accurately.
  1. Exploring AI Tools for Code Improvement Tools like ChatGPT or GitHub Copilot can help refactor or suggest cleaner versions of your code. While these AI-driven suggestions can offer quick pointers, you should still understand underlying clean-code principles so you can judge whether AI’s proposals align with best practices.

Interesting Quotes and Stories

  • "It should be part of your definition of 'done' to ensure the code is clean." — This captures Bob’s stance on integrating clean code into the development process from the outset.
  • "Errors should not pass silently." — A nod to the Zen of Python and how broad exception handling can mask real issues.
  • "If it’s not automated, it’s broken." — Emphasizing that style and testing checks should be automated locally before your code makes it to production.

Key Definitions and Terms

  • Code Smell: A surface indication that something may be wrong in your code; it might still work but hints at deeper design or maintainability problems.
  • Cyclomatic Complexity: A quantitative measure of the number of distinct paths through a function or program; higher means more potential branches and complexity.
  • EAFP (Easier to Ask Forgiveness than Permission): Python’s common style of trying an operation and handling the exception if it fails, rather than pre-checking conditions with if-statements.
  • Guard Clause: A pattern that checks for invalid or special conditions early in a function and returns immediately, avoiding deep nesting.

Learning Resources

Here are some curated resources to help you deepen your knowledge of clean, Pythonic code and software testing.

Overall Takeaway

Clean code is an ongoing, deliberate practice rather than a single deliverable. By writing smaller, single-responsibility functions, embracing idiomatic Python, minimizing global state, refactoring regularly, and tapping into tools that automate style checks and formatting, you will produce code that’s much easier to maintain and share with your team. Ultimately, the payoff of writing clean code is fewer bugs, less frustration, and more time spent creating real value.

Links from the show

Bob on Mastodon: @bbelderbos@fosstodon.org
PyBites: pybit.es
Tips for clean code in Python article: pybit.es
Refactoring book: pybitesbooks.com
Final type: docs.python.org
Sentinels pattern: python-patterns.guide
Black formater: pypi.org
Guarding clauses: medium.com
ChatGPT: chat.openai.com
Git Precommit: pre-commit.com
#100DaysOfCode in Python course: training.talkpython.fm
#100DaysOfWeb in Python course: training.talkpython.fm
Watch this episode on YouTube: youtube.com
Episode transcripts: talkpython.fm

--- Stay in touch with us ---
Subscribe to Talk Python on YouTube: youtube.com
Talk Python on Bluesky: @talkpython.fm at bsky.app
Talk Python on Mastodon: talkpython
Michael on Bluesky: @mkennedy.codes at bsky.app
Michael on Mastodon: mkennedy

Talk Python's Mastodon Michael Kennedy's Mastodon