关于异常处理:python try else

Python try-else

try声明中的可选else条款的预期用途是什么?


如果执行从try的底部脱落,则执行else块中的语句—如果没有例外。老实说,我从来没有找到需要。

但是,处理异常注意事项:

The use of the else clause is better
than adding additional code to the try
clause because it avoids accidentally
catching an exception that wasn’t
raised by the code being protected by
the try ... except statement.

因此,如果您有一个方法可以,例如,抛出一个IOError,并且您希望捕获它引发的异常,但是如果第一个操作成功,并且您不希望从该操作中捕获一个ioerror,那么您可能会编写如下内容:

1
2
3
4
5
6
7
8
9
    try:
        operation_that_can_throw_ioerror()
    except IOError:
        handle_the_exception_somehow()
    else:
         # we don't want to catch the IOError if it's raised
        another_operation_that_can_throw_ioerror()
    finally:
        something_we_always_need_to_do()

如果你把another_operation_that_can_throw_ioerror()放在operation_that_can_throw_ioerror之后,except会捕捉到第二个调用的错误。如果你把它放在整个try块之后,它就会一直运行,直到finally块之后。else让你确定

  • 第二个操作只有在没有异常的情况下才运行,
  • 它在finally街区前运行,并且
  • 这里没有抓到它所升高的任何一个IOError

  • 使用else样式和可读性有一个很大的原因。通常,最好保留代码,这样可以在处理它们的代码附近引起异常。例如,比较以下内容:

    1
    2
    3
    4
    5
    6
    try:
        from EasyDialogs import AskPassword
        # 20 other lines
        getpass = AskPassword
    except ImportError:
        getpass = default_getpass

    1
    2
    3
    4
    5
    6
    7
    try:
        from EasyDialogs import AskPassword
    except ImportError:
        getpass = default_getpass
    else:
        # 20 other lines
        getpass = AskPassword

    except不能提前返回或重新抛出异常时,第二个是好的。如果可能的话,我会写:

    1
    2
    3
    4
    5
    6
    7
    8
    try:
        from EasyDialogs import AskPassword
    except ImportError:
        getpass = default_getpass
        return False  # or throw Exception('something more descriptive')

    # 20 other lines
    getpass = AskPassword

    注意:答案是从最近发布的副本复制过来的,因此所有这些"askpassword"的东西。


    一种用法:测试一些应该引发异常的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    try:
        this_should_raise_TypeError()
    except TypeError:
        pass
    except:
        assert False,"Raised the wrong exception type"
    else:
        assert False,"Didn't raise any exception"

    (该代码应该在实践中抽象为更通用的测试。)


    Python try-else

    What is the intended use of the optional else clause of the try statement?

    总结

    如果没有异常,并且没有被returncontinuebreak语句中断,则运行else语句。

    另一个答案错过了最后一部分。

    来自文档:

    The optional else clause is executed if and when control flows off the
    end of the try clause.*

    (加上粗体)脚注如下:

    *Currently, control"flows off the end" except in the case of an
    exception or the execution of a return, continue, or break statement.

    它至少需要一个前导的except子句(参见语法)。因此,它实际上不是"尝试其他",而是"尝试其他"(最后),因为elsefinally是可选的。

    python教程详细介绍了预期的用法:

    The try ... except statement has an optional else clause, which, when
    present, must follow all except clauses. It is useful for code that
    must be executed if the try clause does not raise an exception. For
    example:

    1
    2
    3
    4
    5
    6
    7
    8
    for arg in sys.argv[1:]:
        try:
            f = open(arg, 'r')
        except IOError:
            print 'cannot open', arg
        else:
            print arg, 'has', len(f.readlines()), 'lines'
            f.close()

    The use of the else clause is better than adding additional code to
    the try clause because it avoids accidentally catching an exception
    that wasn’t raised by the code being protected by the try ... except
    statement.

    区分elsetry块后代码的示例

    如果处理错误,则不会运行else块。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    def handle_error():
        try:
            raise RuntimeError('oops!')
        except RuntimeError as error:
            print('handled a RuntimeError, no big deal.')
        else:
            print('if this prints, we had no error!') # won't print!
        print('And now we have left the try block!')  # will print!

    现在,

    1
    2
    3
    >>> handle_error()
    handled a RuntimeError, no big deal.
    And now we have left the try block!


    Try except else非常适合将EAFP模式与duck类型相结合:

    1
    2
    3
    4
    5
    6
    7
    try:
      cs = x.cleanupSet
    except AttributeError:
      pass
    else:
      for v in cs:
        v.cleanup()

    你可能会这么认为吗?VE代码很好:

    1
    2
    3
    4
    5
    try:
      for v in x.cleanupSet:
        v.clenaup()
    except AttributeError:
      pass

    这是在代码中意外隐藏严重错误的好方法。我在那里输入了"清理",但会让我知道的属性错误被吞噬了。更糟糕的是,如果我正确地编写了它,但是清理方法偶尔会被传递给一个具有错误命名属性的用户类型,导致它在中途默默地失败并使文件保持未关闭状态呢?祝你调试的时候好运。


    我发现当你有清理工作要做的时候它真的很有用,即使有例外:

    1
    2
    3
    4
    5
    6
    7
    8
    try:
        data = something_that_can_go_wrong()
    except Exception as e: # yes, I know that's a bad way to do it...
        handle_exception(e)
    else:
        do_stuff(data)
    finally:
        clean_up()

    即使你现在想不出它的用途,你也可以打赌一定有它的用途。这是一个缺乏想象力的样本:

    使用else时:

    1
    2
    3
    4
    5
    6
    7
    a = [1,2,3]
    try:
        something = a[2]
    except:
        print"out of bounds"
    else:
        print something

    else时:

    1
    2
    3
    4
    5
    6
    7
    try:
        something = a[2]
    except:
        print"out of bounds"

    if"something" in locals():
        print something

    这里定义了变量something,如果没有抛出错误。您可以在try块之外删除它,但是如果定义了一个变量,那么它需要一些混乱的检测。


    来自错误和异常处理异常-docs.python.org

    The try ... except statement has an optional else clause, which,
    when present, must follow all except clauses. It is useful for code
    that must be executed if the try clause does not raise an exception.
    For example:

    1
    2
    3
    4
    5
    6
    7
    8
    for arg in sys.argv[1:]:
        try:
            f = open(arg, 'r')
        except IOError:
            print 'cannot open', arg
        else:
            print arg, 'has', len(f.readlines()), 'lines'
            f.close()

    The use of the else clause is better than adding additional code to
    the try clause because it avoids accidentally catching an exception
    that wasn’t raised by the code being protected by the try ... except
    statement.


    在PEP 380中有一个很好的try-else的例子。基本上,它归结为在算法的不同部分执行不同的异常处理。

    就像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    try:
        do_init_stuff()
    except:
        handle_init_suff_execption()
    else:
        try:
            do_middle_stuff()
        except:
            handle_middle_stuff_exception()

    这允许您编写更接近异常发生位置的异常处理代码。


    从python引用来看,似乎else是在try之后执行的,没有例外。如果控件从try子句的结尾流出并且流出时,将执行可选的else子句。2.除上述条款外,其他条款中的例外情况不予处理。

    深入到python有一个例子,如果我理解正确,在try块中,他们试图导入一个模块,当失败时,您会得到异常和绑定默认值,但当它工作时,您可以选择进入else块并绑定所需的内容(参见链接中的示例和解释)。

    如果您试图在catch块中进行工作,可能会引发另一个例外——我想这就是else块的用处所在。


    大多数答案似乎集中在为什么我们不能把材料放在try子句本身的else子句中。try语句中的question else子句…为什么else子句代码不能跟踪try块本身,而这个问题被复制到这个问题上,这有什么好处,但是我在这里没有看到对这个问题的明确答复。我觉得https://stackoverflow.com/a/3996378/1503120很好地回答了这个问题。我还试图在https://stackoverflow.com/a/22579805/1503120中阐明各种条款的各种意义。


    就是这样。try except子句的"else"块适用于在(并且仅当)尝试的操作成功时运行的代码。它可以被使用,也可以被滥用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    try:
        fp= open("configuration_file","rb")
    except EnvironmentError:
        confdata= '' # it's ok if the file can't be opened
    else:
        confdata= fp.read()
        fp.close()

    # your code continues here
    # working with (possibly empty) confdata

    就我个人而言,我喜欢它,并在适当的时候使用它。它在语义上对语句进行分组。


    也许一个用途是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #debug = []

    def debuglog(text, obj=None):
       " Simple little logger."
        try:
            debug   # does global exist?
        except NameError:
            pass    # if not, don't even bother displaying
        except:
            print('Unknown cause. Debug debuglog().')
        else:
            # debug does exist.
            # Now test if you want to log this debug message
            # from caller"obj"
            try:
                if obj in debug:
                    print(text)     # stdout
            except TypeError:
                print('The global"debug" flag should be an iterable.')
            except:
                print('Unknown cause. Debug debuglog().')

    def myfunc():
        debuglog('Made it to myfunc()', myfunc)

    debug = [myfunc,]
    myfunc()

    也许这也会让你发挥作用。


    else块通常可用于补充每个except块中出现的功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    try:
        test_consistency(valuable_data)
    except Except1:
        inconsistency_type = 1
    except Except2:
        inconsistency_type = 2
    except:
        # Something else is wrong
        raise
    else:
        inconsistency_type = 0

    """
    Process each individual inconsistency down here instead of
    inside the except blocks. Use 0 to mean no inconsistency.
    """

    在这种情况下,除了块外,每个块中都设置了inconsistency_type,以便在else中的无错误情况下对行为进行补充。

    当然,我将此描述为一种模式,有一天可能会出现在您自己的代码中。在这种特定情况下,您只需在try块之前将inconsistency_type设置为0。


    我能想到的一个使用场景是不可预知的异常,如果您再试一次,就可以绕过这些异常。例如,当try块中的操作涉及随机数时:

    1
    2
    3
    4
    5
    6
    7
    8
    while True:
        try:
            r = random.random()
            some_operation_that_fails_for_specific_r(r)
        except Exception:
            continue
        else:
            break

    但是如果可以预测异常,那么您应该总是事先选择验证而不是异常。然而,并不是所有的事情都可以预测,所以这个代码模式有它的位置。


    我发现else对于处理可能不正确的配置文件很有用:

    1
    2
    3
    4
    5
    6
    7
    try:
        value, unit = cfg['lock'].split()
    except ValueError:
        msg = 'lock monitoring config must consist of two words separated by white space'
        self.log('warn', msg)
    else:
         # get on with lock monitoring if config is ok

    读取lock配置时出现异常,禁用锁监控,valueerrors记录一条有用的警告消息。


    我发现try: ... else:构造在运行数据库查询并将这些查询的结果记录到具有相同风格/类型的单独数据库的情况下非常有用。假设我有很多工作线程,所有这些线程都处理提交到队列的数据库查询。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #in a long running loop
    try:
        query = queue.get()
        conn = connect_to_db(<main db>)
        curs = conn.cursor()
        try:
            curs.execute("<some query on user input that may fail even if sanitized">)
        except DBError:
            logconn = connect_to_db(<logging db>)
            logcurs = logconn.cursor()
            logcurs.execute("<update in DB log with record of failed query")
            logcurs.close()
            logconn.close()
        else:

            #we can't put this in main try block because an error connecting
            #to the logging DB would be indistinguishable from an error in
            #the mainquery

            #We can't put this after the whole try: except: finally: block
            #because then we don't know if the query was successful or not

            logconn = connect_to_db(<logging db>)
            logcurs = logconn.cursor()
            logcurs.execute("<update in DB log with record of successful query")
            logcurs.close()
            logconn.close()
            #do something in response to successful query
    except DBError:
        #This DBError is because of a problem with the logging database, but
        #we can't let that crash the whole thread over what might be a
        #temporary network glitch
    finally:
        curs.close()
        conn.close()
        #other cleanup if necessary like telling the queue the task is finished

    当然,如果您能够区分可能引发的异常,您就不必使用它,但是如果对一段成功代码做出反应的代码可能会抛出与成功代码相同的异常,那么您就不能让第二个可能的异常消失,或者在成功后立即返回(这会杀死我的CA中的线程)。SE),这确实很有用。


    假设您的编程逻辑取决于字典是否有带有给定键的条目。您可以使用if... else...构造测试dict.get(key)的结果,也可以执行以下操作:

    1
    2
    3
    4
    5
    6
    try:
        val = dic[key]
    except KeyError:
        do_some_stuff()
    else:
        do_some_stuff_with_val()

    这里是另一个我喜欢使用这种模式的地方:

    1
    2
    3
    4
    5
    6
    7
     while data in items:
         try
            data = json.loads(data)
         except ValueError as e:
            log error
         else:
            # work on the `data`


    else:块令人困惑,几乎毫无用处。它也是forwhile声明的一部分。

    事实上,即使是在if声明中,else:也可能被滥用,造成非常难以发现的错误。

    考虑一下这个。

    1
    2
    3
    4
    5
    6
    7
       if a < 10:
           # condition stated explicitly
       elif a > 10 and b < 10:
           # condition confusing but at least explicit
       else:
           # Exactly what is true here?
           # Can be hard to reason out what condition is true

    再想一想else:。这通常是个问题。除非是在if声明中,否则不要这样做,甚至可以考虑记录else条件,使其明确化。