在Python中,“try”语句的可选“else”子句的预期用途是什么?

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

458595 次浏览

如果执行从try的底部掉下来,else块中的语句就会被执行——如果没有例外的话。老实说,我从来没有发现过需要。

但是,处理异常指出:

使用其他子句更好 而不是在尝试中添加额外的代码 因为它避免了意外 抓住了一个例外 由受保护的代码引发

尝试…除了语句。

因此,如果你有一个方法可以,例如,抛出一个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_ioerror之后,except会捕获第二次调用的错误。如果你把它放在整个try块之后,它将永远运行,直到finally之后。else可以让你确保

  1. 第二个操作只有在没有例外的情况下才会运行,
  2. 它在finally块之前运行,并且
  3. 它提出的任何#EYZ都不会在这里被捕获

查看python参考,似乎else在没有异常的情况下在try之后执行。 如果并且当控制从try子句的末尾流出时,可选的else子句将被执行。2 else子句中的异常不会被前面的除非子句处理。

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

如果你试图在catch块中工作,它可能会抛出另一个异常——我想这就是else块派上用场的地方。

尽管你现在想不出它的用途,但你可以打赌它一定有用途。这是一个缺乏想象力的例子:

使用else

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

没有else

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


if "something" in locals():
print(something)

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

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

实际上,即使在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语句中,甚至考虑记录else条件以使其明确。

我发现当你有清理工作要做时,即使有例外也必须完成,这真的很有用:

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

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

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

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

就是这样。try-除外子句的'else'块存在于尝试操作成功时(且仅当)运行的代码中。它可以被使用,也可以被滥用。

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

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

通常可以存在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被设置在每个除了块中,以便在else中的无错误情况下补充行为。

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

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

大概是这样的:

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

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

也许一个用途可能是:

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

也许这也会让你有用武之地。

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

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

使用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”的东西。

我发现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配置的异常会禁用锁监控,并且ValueError会记录一条有用的警告消息。

来自错误和异常#处理异常-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保护的代码引发的…除了 声明。

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

当然,如果你能区分可能抛出的异常,你就不必使用这个,但是如果代码对成功的代码段做出反应可能会抛出与成功的代码段相同的异常,并且你不能让第二个可能的异常消失,或者在成功后立即返回(这会杀死我的线程),那么这确实派上用场。

python try

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

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

此上下文避免意外处理您没有预料到的错误。

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

摘要

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

其他答案忽略了最后一部分。

从文档:

可选的else子句在控制流出时执行 try子句的end。*

(博尔丁补充道。)脚注写道:

*目前,控件“从末尾流出”,除非是 异常或执行returncontinuebreak语句。

它确实需要至少一个前面的除了子句(看到语法)。所以它实际上不是“try-ther”,而是“try-除了-ther(-最后)”,else(和finally)是可选的。

python教程详细说明了预期的用法:

try…除了语句有一个可选的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保护的代码引发的…除了 声明。

区分elsetry块之后的代码的示例

如果您处理错误,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!

假设你的编程逻辑取决于字典是否有一个给定键的条目。你可以使用if... else...构造测试dict.get(key)的结果,或者你可以执行:

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

对于将EAFP模式鸭子打字结合使用,try-exex-ther非常有用:

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

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

我能想到的一个使用场景是不可预测的异常,如果重试可以规避。例如,当try块中的操作涉及随机数时:

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

但是,如果可以预测异常,则应始终选择事先验证而不是异常。然而,并非所有内容都可以预测,因此此代码模式有其位置。

我会添加另一个在处理DB会话时看起来很直接的用例:

    # 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()
try:
statements # statements that can raise exceptions
except:
statements # statements that will be executed to handle exceptions
else:
statements # statements that will be executed if there is no exception

示例:

try:
age=int(input('Enter your age: '))
except:
print ('You have entered an invalid value.')
else:
if age <= 21:
print('You are not allowed to enter, you are too young.')
else:
print('Welcome, you are old enough.')

输出:

>>>
Enter your age: a
You have entered an invalid value.
>>> RESTART
>>>
Enter your age: 25
Welcome, you are old enough.
>>> RESTART
>>>
Enter your age: 13
You are not allowed to enter, you are too young.
>>>

复制自:https://geek-university.com/python/the-try-except-else-statements/

可以使用此构造在finally子句中以通用方式处理异常,同时在没有异常时执行其他操作:

class TooManyRetries(RuntimeError):
pass




n_tries = 0
max_retries = 2
while True:
try:
n_tries += 1
if n_tries >= max_retries:
raise TooManyRetries
fail_prone_operation()
except Exception1 as ex:
# handle1
except Exception2 as ex:
# handle2
except Exception3 as ex:
# handle3
except TooManyRetries as ex:
raise
else: # No exception
n_tries = 0
finally:
common_restore_state()
continue