try-except-in-loop (PERF203)
Derived from the Perflint linter.
What it does
Checks for uses of except handling via try
-except
within for
and while
loops.
Why is this bad?
Exception handling via try
-except
blocks incurs some performance overhead, regardless of whether an exception is raised.
To optimize your code, two techniques are possible:
- Refactor your code to put the entire loop into the
try
-except
block, rather than wrapping each iteration in a separatetry
-except
block. - Use "Look Before You Leap" idioms that attempt to avoid exceptions being raised in the first place, avoiding the need to use
try
-except
blocks in the first place.
This rule is only enforced for Python versions prior to 3.11, which introduced "zero-cost" exception handling. However, note that even on Python 3.11 and newer, refactoring your code to avoid exception handling in tight loops can provide a significant speedup in some cases, as zero-cost exception handling is only zero-cost in the "happy path" where no exception is raised in the try
-except
block.
As with all perflint
rules, this is only intended as a micro-optimization. In many cases, it will have a negligible impact on performance.
Example
string_numbers: list[str] = ["1", "2", "three", "4", "5"] # `try`/`except` that could be moved out of the loop: int_numbers: list[int] = [] for num in string_numbers: try: int_numbers.append(int(num)) except ValueError as e: print(f"Couldn't convert to integer: {e}") break # `try`/`except` used when "look before you leap" idioms could be used: number_names: dict[int, str] = {1: "one", 3: "three", 4: "four"} for number in range(5): try: name = number_names[number] except KeyError: continue else: print(f"The name of {number} is {name}")
Use instead:
string_numbers: list[str] = ["1", "2", "three", "4", "5"] int_numbers: list[int] = [] try: for num in string_numbers: int_numbers.append(int(num)) except ValueError as e: print(f"Couldn't convert to integer: {e}") number_names: dict[int, str] = {1: "one", 3: "three", 4: "four"} for number in range(5): name = number_names.get(number) if name is not None: print(f"The name of {number} is {name}")