Python try-else


578

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


1
大多数答案似乎都集中于为什么我们不能仅将材料放在try子句本身的else子句中。问题stackoverflow.com/questions/3996329特别询问了else子句代码为什么不能 try块本身之后进行,并且这个问题被重复到了这个问题上,但是我在这里看不到对该问题的明确答复。我觉得stackoverflow.com/a/3996378/1503120很好地回答了这个问题。我还试图在stackoverflow.com/a/22579805/1503120上阐明各种条款的各种意义。
jamadagni 2014年

如果在最终清理之前没有触发异常,则您希望发生某种事情,而这本身不会触发相同的异常处理。
benjimin

Answers:


857

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

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

使用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. 只有在没有例外的情况下,第二个操作才会运行
  2. 它在代码finally块之前运行,并且
  3. IOError它筹集到的任何钱都没有被抓住

7
还请记住,try块中使用的变量可以在else块中使用,因此,如果您不期望else块中有更多异常,
则应始终

3
没关系,因为在尝试之外可以看到尝试作用域变量,无论是否存在其他变量。
Reinderien 2014年

36
没有“尝试作用域变量”之类的东西。在Python中,变量作用域仅由模块,函数和理解来建立,而不是控制结构来建立。
mhsmith 2015年

9
else子句使您可以编写仅在未引发异常的情况下才有意义的代码。except子句可以简单地通过。如果将逻辑放在try块中,则可能会无声地隐藏代码中的错误。切勿压榨您没想到的异常。
爱丽丝·珀赛尔

9
它不是来自这个答案是什么明确的“脱落的底部”的手段-不仅会出现这种情况,因为一个例外,而且,因为一个returncontinuebreak
Antti Haapala'3

108

有一个重要的用途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”的东西。


53

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

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

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


50

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!

26

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被吞噬了。更糟糕的是,如果我正确编写了该方法,但是偶尔会向该传递给具有错误名称属性的用户类型的清理方法,导致该方法在中途无声地失败并且未关闭文件?祝您调试好运。


19

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

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

9

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

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块外将其删除,但是如果定义了变量,则需要进行一些混乱的检测。


3
something = a[2]; print somethingtry:块内部有什么问题?
S.Lott

@ S.Lott没什么,但是如果有人向您发送列表,并且由于数据可能不够长而又不想显示足够长的数据而又不想显示该怎么办?
未知

12
S. Lott:“打印一些东西”可能会引发另一个您不想拦截的异常。
达里乌斯·培根

我看不出有什么区别。如果出现超出范围的异常,它将显示“超出范围”。知道了。如果我遇到其他异常,则此代码块不会捕获该异常。如果我没有例外,则行为是打印某物的值,即a [2]。在此示例中,我看不到其他功能。
S.Lott

3
当输出“ something”时,其__str __()方法可能会引起错误。尽管在此示例中该值实际上仅为2,但您可能还要指出,这里也没有越界异常。
达里乌斯·培根

8

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

就像这样:

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

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


7

来自错误和异常#处理异常-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语句保护的代码未引发的异常。


6

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

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

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


4
“ else子句中的异常不由前面的except子句处理。” 那是有用的部分。谢谢。
geowa4

另一个区别是,“如果控制从try子句的末尾流出,则将执行可选的else子句”,因为您可以返回该try块。
Tomer W

4

而已。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

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


2

可能的用途可能是:

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

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


2

我发现该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

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


1

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


1

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

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

1
您可以只使用continue“早期爆发”模式。这使您可以删除“ else”子句及其缩进,使代码更易于阅读。
malthe

1

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

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

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


1
您可以这样做,将break内部放到最try底端,这是更干净的IMO,并且不需要else。另外,continue并不是真正需要的,您可以pass
Dirbaio

1

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


1

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

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

-1

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

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

-17

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以使其明确。


6
我不同意这一点。在“ if-elif”块中,“ else”用作“默认”,在C语言的“ case”块中使用。即使您认为您已经涵盖了各种情况下的所有情况,也始终建议处理“默认”情况。
约瑟普(Josip)

1
@Josip:用作“默认值”可能会造成混淆。问题是要明确定义此“默认”条件。错误定义的默认条件可能是错误行为的根本原因。否则可能会造成混乱。在所有情况下都应该仔细考虑一下,不仅要尝试一遍又一遍,而且要尽可能。
S.Lott

5
嗯,上面的代码是完全抽象的,没有做任何有意义的事情,所以,是的,这令人感到困惑。
朱尔克斯(Julx)2011年

1
@ S.Lott“这将减少错误”-我的意思是,这是错误的。我认为我们在意见上存在真正的分歧。糟糕的程序员总会找到编写错误程序的方法。总是。好的程序员总是寻求好的实践,并且可以用几乎任何一种语言编写好的代码。消除有用的构造只会给优秀的程序员带来更少的权力,而不能特别地帮助那些不好的程序员,因为那些程序员能够发明出无数种解决问题的方法。
朱尔克斯2011年

5
考虑:if x > 0: return "yes"if x <= 0: return "no"。现在,一个人来改变一个要说的条件,x > 1却忘记改变另一个条件。如何减少将要提交的错误数量。if else子句有时相隔很多行。实际上,DRY是一种很好的做法,通常很多时候。(重复发帖,抱歉)。
朱尔克斯2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.