Python Style Guide

This is my personal style guide for writing Python code. As with all style guides, this guide is incomplete, fallible, and its recommendations aren't appropriate for every circumstance.

I've only recently started writing this, so it's obviously particularly incomplete. Hopefully, it will grow as I learn and think more!

This guide is focused only on recommendations that aren't universal across programming languages, for such recommendations see my [Programming Style Guide](programming.org::*Programming Style Guide). Additionally, I don't currently publish any recommendations that concern third-party libraries.

Its intended audience is intermediate or advanced Python programmers who endeavor especially to write reliable, correct code. In particular, I'm often willing to err on the side of reliability over "Pythonic" or "idiomatic" code, e.g., by changing code so that it can get more mileage/guarantees from a static type checker like Mypy, or by using less mutation than might be "expected". But to be honest, I mostly publish this so I have somewhere with thorough explanations that I can point people to when I make comments during code reviews.

The recommendations in this guide are split into three levels, based on the author's perception of their relative importance and applicability:

This document aspires to confirm to my Meta Style Guide.

Do you have ideas for this guide? Contact me!

Use Dataclasses

Every class should be annotated with the dataclasses.dataclass decorator (doc) unless there is a specific reason not to. This still applies if you have to write an __init__ method.

  1. Example

    Instead of

    class C:
        def __init__(self, x: int) -> None:
            self.x = x
    

    write

    @dataclass
    class C:
        x: int
    
  2. Justification

    The dataclass decorator can generate all sorts of "dunder" methods that you normally have to write by hand, such as __init__ and __eq__. This saves both the writer and reader of the code some time!

    Dialing in on __eq__: the implementation generated by dataclass compares instances of the class field-wise. This is almost universally more meaningful than pointer/reference equality, which is the default for classes that inherit from object. Consider this example:

    C(5) == C(5)
    

    With the former implementation of C, this evaluates to False whereas with the dataclass version it evaluates to True. If you think this is unimportant because you don't intend to compare instances of your class for equality, consider overriding the __eq__ method manually and raising an exception if it gets called to make that assumption explicit. If you don't do that, strongly consider using dataclass.

Nice to Have