新版本的变更 / Python 3.11

def load(name):
    if name == "bad":
        raise ValueError("cannot parse " + name)
    if name == "missing":
        raise FileNotFoundError(name)
    return len(name)


# *** before: ***
# several failures at once had to be packed into one exception by hand,
# and the caller unpacked the list itself - try/except could not pick out
# the kind of error it knew how to handle
class MultiError(Exception):
    def __init__(self, errors):
        super().__init__(errors)
        self.errors = errors


def load_all_old(names):
    total, errors = 0, []
    for name in names:
        try:
            total += load(name)
        except Exception as error:
            errors.append(error)
    if errors:
        raise MultiError(errors)
    return total


try:
    load_all_old(["alpha""bad""missing"])
except MultiError as group:
    for error in group.errors:
        print("old:"type(error).__name__, error)


# *** in version 3.11: ***
def load_all(names):
    total, errors = 0, []
    for name in names:
        try:
            total += load(name)
        except Exception as error:
            errors.append(error)
    if errors:
        raise ExceptionGroup("loading failed", errors)
    return total


# except* takes ITS part of the group and leaves the rest to the others,
# so more than one branch may run for a single raise
try:
    load_all(["alpha""bad""missing"])
except* ValueError as group:
    print("values:", [str(e) for e in group.exceptions])
except* FileNotFoundError as group:
    print("missing:", [str(e) for e in group.exceptions])

# a plain except catches the WHOLE group, not a part of it
try:
    load_all(["bad"])
except ExceptionGroup as group:
    print("whole:", group.messagelen(group.exceptions))