PEP 8 Programming Recommendations: is None, Bare except, Lambdas and Other Habits

The last major section of PEP 8 is different from the rest. Instead of spaces and line lengths, it collects habits that make Python code more correct and easier to reason about. Several of them are checked by linters; all of them are worth understanding, because the reasons matter more than the rule.
All examples below were run with Python 3.13.
Comparisons to None and other singletons
"Comparisons to singletons like None should always be done with is or is not, never the equality operators." The reason is that == calls the object's __eq__ method, which a class can define however it likes, while is checks identity and cannot be overridden:
class Anything:
def __eq__(self, other):
return True
value = Anything()
print(value == None)
print(value is None)
True
False
pycodestyle reports == None as E711 and comparisons with True or False as E712. PEP 8 also prefers if foo is not None: to if not foo is None: (E714), and if x not in y to if not x in y (E713): the two forms behave identically, but the first reads like the sentence it means.
Truthiness, and when not to rely on it
For sequences, PEP 8 recommends using the fact that empty ones are false: if not seq: rather than if len(seq) == 0:. Likewise, do not compare Booleans to True or False: write if greeting:, not if greeting == True:, and certainly not if greeting is True:, which the guide calls worse.
The same section adds a warning in the other direction: beware of writing if x when you really mean if x is not None. A parameter that defaults to None may receive a value that is itself false:
count = 0
if count:
print("set")
if count is not None:
print("count was provided")
count was provided
Zero, an empty string or an empty list are real values. If the question is "was an argument passed?", test against None.
Name functions with def, not lambda
"Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier." A lambda's only advantage is that it can be written inside a larger expression; once you assign it to a name, that advantage is gone, and you lose a useful name in tracebacks and in the function's representation:
square = lambda x: x * x
def cube(x):
return x ** 3
print(square.__name__, cube.__name__)
<lambda> cube
pycodestyle reports the assignment as E731. Lambdas remain perfectly idiomatic as arguments, for example sorted(rows, key=lambda row: row.date).
Exceptions: specific, small and chained
PEP 8 has several recommendations about exceptions, and they work together.
- Derive your exceptions from
Exception, notBaseException, and design the hierarchy around what calling code needs to distinguish. Name error classes with the suffixError. - Catch specific exceptions. A bare
except:also catchesSystemExitandKeyboardInterrupt, "making it harder to interrupt a program with Control-C". If you really want every program error, writeexcept Exception:. pycodestyle reports a bare except as E722. PEP 8 accepts bare except in two cases: when the handler logs or prints the traceback, or when it cleans up and re-raises. - Keep the
tryblock minimal, so you do not accidentally catch an error raised by code you did not mean to protect. Theelseclause is the tool for this:
def lookup(table, key):
try:
value = table[key]
except KeyError:
return None
else:
return value.upper()
print(lookup({"a": "x"}, "a"), lookup({}, "b"))
X None
If value.upper() raised a KeyError of its own, it would not be mistaken for a missing key.
- Chain exceptions explicitly. Use
raise NewError(...) from excwhen replacing a low-level error with a more meaningful one; the original stays available as__cause__and appears in the traceback. - Use
withto release resources reliably, and give a context manager a descriptive method when it does more than acquire and release, as inwith conn.begin_transaction():.
Do not return from finally
PEP 8 discourages return, break or continue inside a finally block when they would jump out of it, because they silently cancel any exception in flight:
def risky():
try:
1 / 0
finally:
return 42
print(risky())
42
The ZeroDivisionError simply disappears. PEP 765, implemented in Python 3.14, makes the compiler emit a SyntaxWarning for this pattern.
Be consistent in return statements
"Either all return statements in a function should return an expression, or none of them should." If one path returns a value, the others should say return None explicitly, and a function that can fall off the end should end with an explicit return. A reader then knows that None is a deliberate result, not a forgotten branch. pylint reports violations as inconsistent-return-statements.
Prefer the explicit method
- Use
''.startswith()and''.endswith()instead of slicing:name.startswith("test_")is clearer thanname[:5] == "test_"and cannot get the length wrong. Both methods accept a tuple of alternatives:name.startswith(("test_", "check_")). - Use
isinstance()rather than comparing types. For a subclass offloat,type(t) == floatisFalsewhileisinstance(t, float)isTrue. pycodestyle reports direct type comparisons as E721. - When implementing ordering, define all six rich comparison methods, or let
functools.total_orderinggenerate the missing ones. - Do not rely on CPython's in-place string concatenation being fast; in performance-sensitive code, build a list and use
''.join().
Annotations
The section ends with style rules for function and variable annotations: follow PEP 484 syntax, put a single space after the colon and none before it, and spaces around = when an annotated name has a default. Our guide to type hints covers the rest.
Where tools help
The checker on our home page reports E711, E712, E713, E714, E721, E722 and E731. Inconsistent returns, overly broad try blocks and the misuse of truthiness need a linter with more semantic analysis, such as pylint, or a human reviewer; our code review checklist includes them.
Sources
- PEP 8, "Programming Recommendations", "Function Annotations" and "Variable Annotations".
- Python Tutorial, "Errors and Exceptions" (else clause, exception chaining).
- PEP 765 – Disallow return/break/continue that exit a finally block, Python 3.14.
- pycodestyle error codes E711 to E731; pylint message
inconsistent-return-statements.
PEP 8, section by sectionPEP 8, explained section by section
What the official Python style guide actually says, section by section, which parts tools can check, and where it tells you to use your judgment.
Readable code in practicePython code review checklist
Automate the style, then review what tools cannot see: behaviour, tests, names, error handling and security, plus how to phrase comments people can act on.
PEP 8, section by sectionImports: order and grouping
One import per line, at the top, in three groups: the PEP 8 rules for imports, the exceptions, and how isort or ruff sort them for you.