Python try-else

问题:Python try-else

语句的可选else子句的预期用途是try什么?

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


回答 0

else如果执行不符合try-如果没有exceptions,则执行块中的语句。老实说,我从来没有发现需要。

但是,“ 处理异常”指出:

使用else子句比向try子句添加其他代码更好,因为它避免了意外捕获try … except语句保护的代码未引发的异常。

所以,如果你有一个方法可以,例如,抛出IOError了,你想抓住它会引发异常,但有你想,如果第一个操作成功做其它的事情,你不要想抓住从一个IOError该操作,您可能会这样写:

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_ioerrorexcept则将捕获第二个调用的错误。并且,如果将其放在整个代码try块之后,它将始终运行,直到finally。将else让您确保

  1. 只有在没有exceptions的情况下,第二个操作才会运行
  2. 它在代码finally块之前运行,并且
  3. IOError它筹集到的任何钱都没有被抓住

The statements in the else block are executed if execution falls off the bottom of the try – if there was no exception. Honestly, I’ve never found a need.

However, Handling Exceptions notes:

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.

So, if you have a method that could, for example, throw an IOError, and you want to catch exceptions it raises, but there’s something else you want to do if the first operation succeeds, and you don’t want to catch an IOError from that operation, you might write something like this:

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()

If you just put another_operation_that_can_throw_ioerror() after operation_that_can_throw_ioerror, the except would catch the second call’s errors. And if you put it after the whole try block, it’ll always be run, and not until after the finally. The else lets you make sure

  1. the second operation’s only run if there’s no exception,
  2. it’s run before the finally block, and
  3. any IOErrors it raises aren’t caught here

回答 1

有一个重要的用途else-风格和可读性。通常,将可能导致异常的代码保留在处理它们的代码附近是一个好主意。例如,比较这些:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

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

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

注:答案抄自最近发布的重复这里,所以这一切的“AskPassword”的东西。

There is one big reason to use else – style and readability. It’s generally a good idea to keep code that can cause exceptions near the code that deals with them. For example, compare these:

try:
    from EasyDialogs import AskPassword
    # 20 other lines
    getpass = AskPassword
except ImportError:
    getpass = default_getpass

and

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
else:
    # 20 other lines
    getpass = AskPassword

The second one is good when the except can’t return early, or re-throw the exception. If possible, I would have written:

try:
    from EasyDialogs import AskPassword
except ImportError:
    getpass = default_getpass
    return False  # or throw Exception('something more descriptive')

# 20 other lines
getpass = AskPassword

Note: Answer copied from recently-posted duplicate here, hence all this “AskPassword” stuff.


回答 2

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

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

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

One use: test some code that should raise an exception.

try:
    this_should_raise_TypeError()
except TypeError:
    pass
except:
    assert False, "Raised the wrong exception type"
else:
    assert False, "Didn't raise any exception"

(This code should be abstracted into a more generic test in practice.)


回答 3

Python try-else

elsetry语句的optional 子句的预期用途是什么?

如果没有预期处理异常的情况,则预期的用途是为更多代码运行提供上下文。

此上下文避免了意外处理您未预期到的错误。

但重要的是要了解导致else子句来运行的精确条件,因为returncontinuebreak可中断的控制流else

综上所述

else声明是否有运行没有异常,如果不是被打断returncontinuebreak声明。

其他答案错过了最后一部分。

从文档:

可选的else,如果条款被执行,并且当控制切断端流动的的try条款。*

(加粗体。)脚注如下:

*目前,控制“落端流动”除了在异常或一个的执行的情况下returncontinuebreak语句。

它确实需要至少一个前面的except子句(请参阅语法)。因此,它实际上不是“ try-else”,而是“ try-except-else(-finally)”,else(和finally)是可选的。

Python指南预期的使用详解:

try … except语句具有可选的else子句,该子句在存在时必须遵循所有except子句。这对于try子句未引发异常的必须执行的代码很有用。例如:

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()

使用else子句比向try子句添加其他代码更好,因为它避免了意外捕获try … except语句保护的代码未引发的异常。

else后面的示例区分代码try

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

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!

现在,

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

Python try-else

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

The intended use is to have a context for more code to run if there were no exceptions where it was expected to be handled.

This context avoids accidentally handling errors you did not expect.

But it’s important to understand the precise conditions that cause the else clause to run, because return, continue, and break can interrupt the control flow to else.

In Summary

The else statement runs if there are no exceptions and if not interrupted by a return, continue, or break statement.

The other answers miss that last part.

From the docs:

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

(Bolding added.) And the footnote reads:

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

It does require at least one preceding except clause (see the grammar). So it really isn’t “try-else,” it’s “try-except-else(-finally),” with the else (and finally) being optional.

The Python Tutorial elaborates on the intended usage:

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:

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.

Example differentiating else versus code following the try block

If you handle an error, the else block will not run. For example:

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!

And now,

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

回答 4

Try-except-else非常适合将EAFP模式鸭子模式结合使用:

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

您可能会觉得此朴素的代码很好:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

这是一种意外隐藏代码中严重错误的好方法。我在这里打错了清理,但是让我知道的AttributeError被吞噬了。更糟糕的是,如果我正确编写了该方法,但是偶尔会向该传递给具有错误名称属性的用户类型的清理方法,导致该方法在中途无声地失败并且未关闭文件?祝您调试好运。

Try-except-else is great for combining the EAFP pattern with duck-typing:

try:
  cs = x.cleanupSet
except AttributeError:
  pass
else:
  for v in cs:
    v.cleanup()

You might thing this naïve code is fine:

try:
  for v in x.cleanupSet:
    v.clenaup()
except AttributeError:
  pass

This is a great way of accidentally hiding severe bugs in your code. I typo-ed cleanup there, but the AttributeError that would let me know is being swallowed. Worse, what if I’d written it correctly, but the cleanup method was occasionally being passed a user type that had a misnamed attribute, causing it to silently fail half-way through and leave a file unclosed? Good luck debugging that one.


回答 5

我发现当您必须执行清理操作时,即使有exceptions,它也确实很有用:

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()

I find it really useful when you’ve got cleanup to do that has to be done even if there’s an exception:

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()

回答 6

即使您现在无法想到它的用途,您也可以打赌一定有它的用途。这是一个难以想象的示例:

else

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

没有else

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

something如果没有错误抛出,则在此处定义了变量。您可以在try块外将其删除,但是如果定义了变量,则需要进行一些混乱的检测。

Even though you can’t think of a use of it right now, you can bet there has to be a use for it. Here is an unimaginative sample:

With else:

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

Without else:

try:
    something = a[2]
except:
    print "out of bounds"

if "something" in locals():
    print something

Here you have the variable something defined if no error is thrown. You can remove this outside the try block, but then it requires some messy detection if a variable is defined.


回答 7

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

就像这样:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

这使您可以在发生异常的地方编写异常处理代码。

There’s a nice example of try-else in PEP 380. Basically, it comes down to doing different exception handling in different parts of the algorithm.

It’s something like this:

try:
    do_init_stuff()
except:
    handle_init_suff_execption()
else:
    try:
        do_middle_stuff()
    except:
        handle_middle_stuff_exception()

This allows you to write the exception handling code nearer to where the exception occurs.


回答 8

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

try ... except语句有一个可选else子句,当存在时,该子句必须遵循所有除子句之外的子句。这对于try子句未引发异常的必须执行的代码很有用。例如:

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()

使用else子句比向try子句添加其他代码更好,因为它避免了意外捕获try … except语句保护的代码未引发的异常。

From Errors and Exceptions # Handling exceptions – 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:

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.


回答 9

查看Python参考,似乎else是在try没有异常的情况下执行的。当控制从try子句的末尾流出时,将执行可选的else子句。2 else子句中的异常不由前面的except子句处理。

深入研究python有一个例子,如果我理解正确,try他们会在块中尝试导入模块,当该模块失败时,您将获得异常并绑定默认值,但是当它起作用时,您可以选择进入else块并绑定所需的内容(请参阅示例和说明的链接)。

如果您尝试在代码catch块中进行工作,则可能会引发另一个异常-我想这就是代码else块派上用场的地方。

Looking at Python reference it seems that else is executed after try when there’s no exception. The optional else clause is executed if and when control flows off the end of the try clause. 2 Exceptions in the else clause are not handled by the preceding except clauses.

Dive into python has an example where, if I understand correctly, in try block they try to import a module, when that fails you get exception and bind default but when it works you have an option to go into else block and bind what is required (see link for the example and explanation).

If you tried to do work in catch block it might throw another exception – I guess that’s where the else block comes handy.


回答 10

而已。try-except子句的’else’块存在于(仅当)tryed操作成功时运行的代码。它可以被使用,也可以被滥用。

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

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

That’s it. The ‘else’ block of a try-except clause exists for code that runs when (and only when) the tried operation succeeds. It can be used, and it can be abused.

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

Personally, I like it and use it when appropriate. It semantically groups statements.


回答 11

可能的用途可能是:

#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()

也许这会导致您使用过多。

Perhaps a use might be:

#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()

Maybe this will lead you too a use.


回答 12

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

#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

当然,如果您可以区分可能引发的异常,则不必使用它,但是如果对成功的一段代码做出反应的代码可能会抛出与该成功的段相同的异常,并且您不能仅仅让第二个可能的异常消失,或者在成功后立即返回(在我的情况下这将杀死线程),这确实派上了用场。

I have found the try: ... else: construct useful in the situation where you are running database queries and logging the results of those queries to a separate database of the same flavour/type. Let’s say I have lots of worker threads all handling database queries submitted to a queue

#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

Of course if you can distinguish between the possible exceptions that might be thrown, you don’t have to use this, but if code reacting to a successful piece of code might throw the same exception as the successful piece, and you can’t just let the second possible exception go, or return immediately on success (which would kill the thread in my case), then this does come in handy.


回答 13

else通常可以使用一个块来补充每个except块中发生的功能。

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在每个except块中都设置,以便在中的无错误情况下补充行为else

当然,我将其描述为一种模式,有一天可能会在您自己的代码中出现。在这种情况下,无论如何您只需将其设置inconsistency_type为0 try

An else block can often exist to complement functionality that occurs in every except block.

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.
"""

In this case, inconsistency_type is set in each except block, so that behaviour is complemented in the no-error case in else.

Of course, I’m describing this as a pattern that may turn up in your own code someday. In this specific case, you just set inconsistency_type to 0 before the try block anyway.


回答 14

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

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

Here is another place where I like to use this pattern:

 while data in items:
     try
        data = json.loads(data)
     except ValueError as e:
        log error
     else:
        # work on the `data`

回答 15

我可以想到的一种使用场景是不可预测的异常,如果再次尝试可以避免这种异常。例如,当try块中的操作涉及随机数时:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

但是,如果可以预测到异常,则应始终在选择异常之前预先选择验证。但是,并非所有事情都可以预测,因此此代码模式应有的地位。

One of the use scenarios I can think of is unpredictable exceptions, which can be circumvented if you try again. For instance, when the operations in try block involves random numbers:

while True:
    try:
        r = random.random()
        some_operation_that_fails_for_specific_r(r)
    except Exception:
        continue
    else:
        break

But if the exception can be predicted, you should always choose validation beforehand over an exception. However, not everything can be predicted, so this code pattern has its place.


回答 16

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

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会记录一条有用的警告消息。

I have found else useful for dealing with a possibly incorrect config file:

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

An exception reading the lock config disables lock monitoring and ValueErrors log a helpful warning message.


回答 17

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

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

Suppose your programming logic depends on whether a dictionary has an entry with a given key. You can test the result of dict.get(key) using if... else... construct, or you can do:

try:
    val = dic[key]
except KeyError:
    do_some_stuff()
else:
    do_some_stuff_with_val(val)

回答 18

我将添加另一个在处理数据库会话时似乎很简单的用例:

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()

I would add another use case that seems straight forward when handling DB sessions:

    # getting a DB connection 
    conn = db.engine.connect()

    # and binding to a DB session
    session = db.get_session(bind=conn)

    try:
        # we build the query to DB
        q = session.query(MyTable).filter(MyTable.col1 == 'query_val')

        # i.e retrieve one row
        data_set = q.one_or_none()

        # return results
        return [{'col1': data_set.col1, 'col2': data_set.col2, ...}]

    except:
        # here we make sure to rollback the transaction, 
        # handy when we update stuff into DB
        session.rollback()
        raise

    else:
        # when no errors then we can commit DB changes
        session.commit()

    finally:
        # and finally we can close the session
        session.close()

回答 19

else:块令人困惑,并且(几乎)无用。它也是forand while语句的一部分。

实际上,即使在if声明中,else:也可能以真正可怕的方式滥用它们,从而创建很难发现的错误。

考虑一下。

   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-statement中,否则应避免使用它,甚至还要考虑记录-condition else以使其明确。

The else: block is confusing and (nearly) useless. It’s also part of the for and while statements.

Actually, even on an if-statement, the else: can be abused in truly terrible ways creating bugs that are very hard to find.

Consider this.

   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

Think twice about else:. It is generally a problem. Avoid it except in an if-statement and even then consider documenting the else– condition to make it explicit.