在Python中使用try-except-else是一个好习惯吗?
不时在Python,我看到块:
try: try_this(whatever) except SomeException as exception: #Handle exception else: return something
try-except-else存在的原因是什么?
我不喜欢这种编程,因为它使用exception来执行stream量控制。 但是,如果它被包含在语言中,那么一定有一个很好的理由,不是吗?
我的理解是,exception不是错误 ,只能用于例外条件(例如,我试图写入文件到磁盘,没有更多的空间,或者我没有权限),而不是stream控制。
通常我会处理exception:
something = some_default_value try: something = try_this(whatever) except SomeException as exception: #Handle exception finally: return something
或者如果我真的不想在exception发生时返回任何东西,那么:
try: something = try_this(whatever) return something except SomeException as exception: #Handle exception
“我不知道这是否是无知的,但我不喜欢那种编程,因为它使用exception来执行stream量控制。
在Python世界中,使用exception进行stream量控制是常见和正常的。
即使是Python核心开发人员也使用exception来进行stream程控制,而且这种风格已经深入到语言中(即迭代器协议使用StopIteration来标记循环终止)。
另外,try-except-style用于防止某些“先看后跳”构造中固有的竞态条件。 例如,testingos.path.exists会导致在您使用该信息时可能会过时的信息。 同样, Queue.full返回可能陈旧的信息。 在这种情况下, try-except-else样式将产生更可靠的代码。
“我的理解是,例外不是错误,只能用于例外条件”
在其他一些语言中,这一规则反映了他们的图书馆所反映的文化规范。 “规则”也部分基于这些语言的性能考虑。
Python的文化规范有些不同。 在许多情况下,您必须使用控制stream的exception。 此外,在Python中使用exception不会像在某些编译语言中那样减慢周围的代码和调用代码(即,无论您是否实际使用exception,CPython在每个步骤中都已经实现了用于exception检查的代码)。
换句话说,你的理解是“例外是例外的”,这是一个在其他一些语言中是合理的规则,但是对于Python来说是不合适的。
“但是,如果它被包含在语言本身中,那么肯定有一个很好的理由,不是吗?
除了帮助避免竞态条件之外,exception对于抽取外部循环的error handling也非常有用。 这是解释性语言中不需要自动循环不变代码运动的必要优化。
而且,在处理问题的能力远远超出问题出现的地方的常见情况下,exception可以简化代码。 例如,业务逻辑顶级用户界面代码调用代码通常会调用低级例程。 在低级例程(如数据库访问中的唯一键的重复logging)中出现的情况只能在顶级代码中处理(例如询问用户是否与现有密钥不冲突的新密钥)。 这种控制stream程的例外使用允许中级程序完全忽略这个问题,并与stream量控制的这个方面很好的分离。
这里有一个不可或缺的博客文章 。
另外,看到这个StackOverFlow答案: exception真的是例外错误?
“除了别的以外,还有什么原因呢?”
else子句本身很有趣。 它在没有exception但在finally子句之前运行。 这是它的主要目的。
如果没有else子句,在最终化之前运行其他代码的唯一select是将代码添加到try子句的笨拙做法。 这很笨拙,因为它可能会引发代码中的exception,而这些代码并不打算受到try-block的保护。
在定稿之前运行额外的不受保护的代码的用例并不经常出现。 所以,不要期望在发布的代码中看到很多例子。 这是有点罕见的。
else子句的另一个用例是执行在没有发生exception时必须发生并且在处理exception时不发生的动作。 例如:
recip = float('Inf') try: recip = 1 / f(x) except ZeroDivisionError: logging.info('Infinite result') else: logging.info('Finite result')
最后,try-block中else语句的最常见用法是为了一些美化(在相同的缩进级别上alignmentexception结果和非例外结果)。 这个用法总是可选的,并不是绝对必要的。
try-except-else存在的原因是什么?
一个try
块允许你处理一个预期的错误。 except
块应该只能捕获你准备处理的exception。 如果您处理意外的错误,您的代码可能会做错误的事情,并隐藏错误。
如果没有错误,则会执行else
子句,并且通过在try
块中不执行该代码,可以避免捕获意外的错误。 再次,捕捉一个意外的错误可以隐藏错误。
例
例如:
try: try_this(whatever) except SomeException as the_exception: handle(the_exception) else: return something
“除了”套件有两个可选的子句, else
finally
。 所以它实际上是try-except-else-finally
。
只有当try
块没有exception时,“else”才会被评估。 它使我们能够简化下面更复杂的代码:
no_error = None try: try_this(whatever) no_error = True except SomeException as the_exception: handle(the_exception) if no_error: return something
所以如果我们将else
方法与其他方法(可能会产生错误)进行比较,我们会发现它可以节省代码行,并且具有更易读,更易维护,更less错误的代码库。
finally
finally
会执行,不pipe怎样,即使另一行正在用return语句进行评估。
用伪代码分解
这可能有助于打破这种情况,尽可能以最小的forms展示所有function,并带有评论。 假设这个语法是正确的(但不能运行,除非名称被定义)伪代码在函数中。
例如:
try: try_this(whatever) except SomeException as the_exception: handle_SomeException(the_exception) # Handle a instance of SomeException or a subclass of it. except Exception as the_exception: generic_handle(the_exception) # Handle any other exception that inherits from Exception # - doesn't include GeneratorExit, KeyboardInterrupt, SystemExit # Avoid bare `except:` else: # there was no exception whatsoever return something() # if no exception, the "something()" gets evaluated, # but the return will not be executed due to the return in the # finally block below. finally: # this block will execute no matter what, even if no exception, # after "something" is eval'd but before that value is returned # but even if there is an exception. # a return here will hijack the return functionality. eg: return True # hijacks the return in the else clause above
确实,我们可以将代码包含在try
块的else
块中,而不是在没有exception的情况下运行,但是如果代码本身引发了我们正在捕获的那种exception呢? 把它留在try
块中会隐藏这个bug。
我们希望尽量减lesstry
块中的代码行数,以避免捕获我们并不期望的exception,这是因为如果我们的代码失败了,我们希望它大声地失败。 这是最好的做法 。
我的理解是,例外不是错误
在Python中,大多数例外都是错误的。
我们可以使用pydoc查看exception层次结构。 例如,在Python 2中:
$ python -m pydoc exceptions
或Python 3:
$ python -m pydoc builtins
会给我们的层次结构。 我们可以看到,绝大多数Exception
都是错误,尽pipePython使用其中的一些for
循环( StopIteration
)。 这是Python 3的层次结构:
BaseException Exception ArithmeticError FloatingPointError OverflowError ZeroDivisionError AssertionError AttributeError BufferError EOFError ImportError ModuleNotFoundError LookupError IndexError KeyError MemoryError NameError UnboundLocalError OSError BlockingIOError ChildProcessError ConnectionError BrokenPipeError ConnectionAbortedError ConnectionRefusedError ConnectionResetError FileExistsError FileNotFoundError InterruptedError IsADirectoryError NotADirectoryError PermissionError ProcessLookupError TimeoutError ReferenceError RuntimeError NotImplementedError RecursionError StopAsyncIteration StopIteration SyntaxError IndentationError TabError SystemError TypeError ValueError UnicodeError UnicodeDecodeError UnicodeEncodeError UnicodeTranslateError Warning BytesWarning DeprecationWarning FutureWarning ImportWarning PendingDeprecationWarning ResourceWarning RuntimeWarning SyntaxWarning UnicodeWarning UserWarning GeneratorExit KeyboardInterrupt SystemExit
Python并不赞同这样的观点,即exception只能用于例外情况,实际上这个习惯用法是“请求宽恕,而不是许可” 。 这意味着使用例外作为stream量控制的常规部分是完全可以接受的,事实上也是受到鼓励的。
这通常是一件好事,因为以这种方式工作有助于避免一些问题(作为一个明显的例子,竞争条件常常被避免),并且它使代码更易读。
想象一下,你有一种情况,你需要处理一些用户input,但有一个已经被处理的默认值。 try: ... except: ... else: ...
结构使非常可读的代码:
try: raw_value = int(input()) except ValueError: value = some_processed_value else: # no error occured value = process_value(raw_value)
比较它如何在其他语言中工作:
raw_value = input() if valid_number(raw_value): value = process_value(int(raw_value)) else: value = some_processed_value
注意优点。 没有必要检查该值是否有效,并分别parsing它们,一次完成。 代码也遵循更合乎逻辑的进程,主要的代码path是第一,然后是“如果不行,就这样做”。
这个例子自然是有点人为的,但是它显示了这种结构的情况。
在python中使用try-except-else是一个好习惯吗?
答案是这是依赖于上下文的。 如果你这样做:
d = dict() try: item = d['item'] except KeyError: item = 'default'
这表明你不太了解Python。 这个function封装在dict.get
方法中:
item = d.get('item', 'default')
try
/ except
块是一个更直观,更冗长的书写方式,可以用primefaces方法在单行中高效执行。 还有其他的情况是这样的。
但是,这并不意味着我们应该避免所有的exception处理。 在某些情况下,最好避免竞争条件。 不要检查文件是否存在,只要试图打开它,并捕获相应的IOError。 为了简单和可读性,尝试封装或将其排除为apropos。
阅读Python的禅宗 ,认识到有些原则处于紧张状态,并且对过于依赖任何一个陈述的教条保持警惕。
你应该小心使用finally块,因为它与try中使用else块不同,除了。 不pipe尝试的结果如何,finally块都会运行。
In [10]: dict_ = {"a": 1} In [11]: try: ....: dict_["b"] ....: except KeyError: ....: pass ....: finally: ....: print "something" ....: something
正如每个人都注意到使用else块会使您的代码更具可读性,并且只在不抛出exception时才运行
In [14]: try: dict_["b"] except KeyError: pass else: print "something" ....:
每当你看到这个:
try: y = 1 / x except ZeroDivisionError: pass else: return y
甚至这个:
try: return 1 / x except ZeroDivisionError: return None
考虑这个,而不是:
import contextlib with contextlib.suppress(ZeroDivisionError): return 1 / x
OP,你是正确的。 除了Python之外,其他的东西都很难看 。 它会导致另一个stream量控制对象,其中不需要:
try: x = blah() except: print "failed at blah()" else: print "just succeeded with blah"
完全相同的是:
try: x = blah() print "just succeeded with blah" except: print "failed at blah()"
这比其他条款更清晰。 try / except之后的else不会频繁写入,因此需要一些时间来确定其含义。
只因为你可以做一件事,并不意味着你应该做一件事。
许多function已被添加到语言,因为有人认为它可能会派上用场。 麻烦的是,function越多,不太清楚明显的事情就是因为人们通常不使用这些花里胡哨的东西。
只是我在这里5美分。 我必须跟在后面,清理大量开发人员编写的大量代码,他们认为自己很聪明,并且希望以一种超级紧凑,超高效的方式编写代码,而这只会让代码变得糟糕稍后尝试阅读/修改。 我每天select可读性,星期天选两次。