Your function turns two spreadsheet cells into an average. Text that is not a number and a count of zero should both give 0.0. Python 3.14 lets you list both exception types without parentheses:
def average(total: str, count: int) -> float:
try:
return float(total) / count
except ValueError, ZeroDivisionError:
return 0.0
print(average("10", 4)) # 2.5
print(average("ten", 4)) # 0.0, from ValueError
print(average("10", 0)) # 0.0, from ZeroDivisionError
The handler catches either exception. Before 3.14 you had to write
except (ValueError, ZeroDivisionError):. That form still works on every
Python 3 version, with the same meaning.
This article and the three after it cover syntax added in Python 3.14 and 3.15. To run their examples, use Python 3.14 or newer, and 3.15 for the last two.
Keep the parentheses when you name the exception
To keep the exception in a variable with as, put the types in parentheses:
def average_or_reason(total: str, count: int) -> tuple[float, str]:
try:
return float(total) / count, ""
except (ValueError, ZeroDivisionError) as exc:
return 0.0, type(exc).__name__
Without the parentheses, Python 3.14 rejects the file:
SyntaxError: multiple exception types must be parenthesized when using 'as'
A single type needs no parentheses, so except ValueError as exc: is fine.
An old Python 2 line now means something else
In Python 2, except ValueError, e: caught a ValueError and stored it in
e. You may still find that spelling in old code and old answers online.
Python 3.13 and older reject it with a SyntaxError. Python 3.14 accepts it,
and reads it as "catch ValueError or e", where e should be an exception
type:
try:
int("abc")
except ValueError, e:
print("caught")
On Python 3.14 this never prints "caught". To check the handler, Python looks
up the name e, finds nothing, and raises
NameError: name 'e' is not defined. If no exception happens, Python never
checks the handler, so the bug can hide until the first bad input. When you
port such code, write except ValueError as e:.
Older versions cannot read the new spelling
On Python 3.12 and 3.13, a file that contains except ValueError, TypeError:
fails to import with
SyntaxError: multiple exception types must be parenthesized. A version
check in the same file does not help, because Python reads the whole file
before it runs any of it. The last article in this track,
Write Code That Runs on Several Python Versions,
shows what to do instead.
Check your understanding
Your library supports Python 3.12 to 3.15. Should you switch its handlers to except A, B:?
Show answer
Hide answer
No. Python 3.12 and 3.13 cannot parse that spelling, so the library would fail to import there. The parenthesized form means the same thing and works on every version you support. Even once your oldest version is 3.14, working handlers do not need a rewrite.
Check your understanding
On Python 3.14, which is valid, except KeyError, IndexError: or except KeyError, IndexError as exc:?
Show answer
Hide answer
Only the first. Without as, the parentheses are optional on 3.14. With
as, you still need them, as in except (KeyError, IndexError) as exc:.
Going further
The change comes from PEP 758. It also covers except*, which handles
exception groups, so except* ValueError, TypeError: is valid on 3.14. The
new spelling changes only how a handler looks. It catches exactly the same
exceptions as the parenthesized form.