How to improve code clarity in nested try-except-else clauses?

后端 未结 3 749
庸人自扰
庸人自扰 2021-01-20 05:19

Sometimes I have a cascade of different things I can try to accomplish a task, e. g. If I need to get a record I can first try to find the record, and if this fails, I can c

3条回答
  •  礼貌的吻别
    2021-01-20 05:51

    You can use a for loop to successively try variants:

    for task, error in ((find_record, NoSuchRecord), (create_record, CreateFailed)):
        try:
            result = task()
        except error:
            continue
        else:
            break
    else:
        # for..else is only entered if there was no break
        result = tape
    

    If you need an else clause, you can provide it as a separate function:

    for task, error, success in (
        (find_record, NoSuchRecord, lambda: logger.info("Record found")),
        (create_record, CreateFailed, lambda: logger.info("Created a new record"))
    ):
        try:
            result = task()
        except error:
            continue
        else:
            success()
            break
    else:
        result = tape
        logger.info("Using a tape now")
    

    Take note that the default case tape is not part of the variants - this is because it has no failure condition. If it should execute with the variants, it can be added as (lambda: tape, (), lambda: None).


    You can put this all into a function for reuse:

    def try_all(*cases):
        for task, error, success in cases:
            try:
                result = task()
            except error:
                continue
            else:
                success()
                return result
    
    try_all(
        (find_record, NoSuchRecord, lambda: logger.info("Record found")),
        (create_record, CreateFailed, lambda: logger.info("Created a new record")),
        (lambda: tape, (), lambda: logger.info("Using a tape now")),
    )
    

    In case the tuples seem difficult to read, a NamedTuple can be used to name the elements. This can be mixed with plain tuples:

    from typing import NamedTuple, Callable, Union, Tuple
    from functools import partial
    
    class Case(NamedTuple):
        task: Callable
        error: Union[BaseException, Tuple[BaseException, ...]]
        success: Callable
    
    
    try_all(
        Case(
            task=find_record,
            error=NoSuchRecord,
            success=partial(logger.info, "Record found")),
        (
            create_record, CreateFailed,
            partial(logger.info, "Created a new record")),
        Case(
            task=lambda: tape,
            error=(),
            success=partial(logger.info, "Using a tape now")),
    )
    

提交回复
热议问题