使用try是一个好习惯

不时在Python中,我看到该块:

try:
   try_this(whatever)
except SomeException as exception:
   #Handle exception
else:
   return something

try-except-else存在的原因是什么?

我不喜欢那种编程,因为它使用异常来执行流量控制。 但是,如果它被包含在语言中,那么一定有充分的理由,不是吗?

我的理解是,异常不是错误 ,并且它们只能用于特殊条件(例如,我尝试将文件写入磁盘并且没有更多空间,或者我没有权限),并且不适用于流控制。

通常我会处理异常,如下所示:

something = some_default_value
try:
    something = try_this(whatever)
except SomeException as exception:
    #Handle exception
finally:
    return something

或者如果我真的不想在发生异常时返回任何内容,那么:

try:
    something = try_this(whatever)
    return something
except SomeException as exception:
    #Handle exception

“我不知道它是否出于无知,但我不喜欢那种编程,因为它使用异常来执行流量控制。”

在Python世界中,使用异常进行流量控制是常见和正常的。

即使是Python核心开发人员也使用异常来进行流程控制,并且这种风格已经深入到语言中(即迭代器协议使用StopIteration来标记循环终止)。

另外,try-except-style用于防止某些“先看后跳”构造中固有的竞态条件。 例如,测试os.path.exists会导致在您使用它时可能会过时的信息。 同样,Queue.full返回可能陈旧的信息。 在这些情况下,try-except-else样式将产生更可靠的代码。

“我的理解是,例外不是错误,它们只能用于特殊条件”

在其他一些语言中,该规则反映了其图书馆所反映的文化规范。 “规则”也部分基于这些语言的性能考虑。

Python文化规范有些不同。 在许多情况下,您必须使用控制流程的例外。 另外,在Python中使用异常并不会像在某些编译语言中那样减慢周围的代码和调用代码(即,无论您是否实际使用异常,CPython在每个步骤中都已实现异常检查代码)。

换句话说,你理解“异常是例外”是一种在其他语言中有意义的规则,但不适用于Python。

“但是,如果它被包含在语言本身中,那么肯定有一个很好的理由,不是吗?”

除了帮助避免竞态条件之外,异常对于提取错误处理外部循环也非常有用。 这是解释型语言中不需要自动循环不变代码运动的必要优化。

另外,在处理问题的能力远远超出问题产生的常见情况下,异常可以简化代码。 例如,业务逻辑顶级用户界面代码调用代码通常会调用低级例程。 在低级例程中出现的情况(例如数据库访问中唯一键的重复记录)只能在顶级代码中处理(例如询问用户是否有与现有密钥不冲突的新密钥)。 对这种控制流程使用例外允许中级例程完全忽略该问题,并与流量控制的这一方面很好地分离。

这里有一个很好的博客文章,讨论例外的不可或缺性。

此外,请参阅此StackOverFlow答案:异常真的是例外错误吗?

“除此之外,还有什么原因存在?”

else子句本身很有趣。 它在没有异常但在finally子句之前运行。 这是它的主要目的。

如果没有else子句,在完成之前运行其他代码的唯一选择是将代码添加到try子句的笨拙做法。 这很笨拙,因为它可能会引发代码中的异常,而这些异常并不打算受到try-block的保护。

在完成之前运行额外的未受保护代码的用例并不经常出现。 所以,不要期望在发布的代码中看到很多例子。 这很少见。

else子句的另一个用例是执行在没有发生异常时必须发生并且在处理异常时不发生的动作。 例如:

   recip = float('Inf')
   try:
       recip = 1 / f(x)
   except ZeroDivisionError:
       logging.info('Infinite result')
   else:
       logging.info('Finite result')

最后,在try-block中最常见的else子句用于美化(在相同的缩进级别对齐特殊结果和非特殊结果)。 此用途始终是可选的,并非严格必要。


try-except-else存在的原因是什么?

try块允许你处理预期的错误。 except块应该只能捕获你准备处理的异常。 如果您处理意外的错误,您的代码可能会做错误的事情并隐藏错误。

如果没有错误,则会执行else子句,并且通过在try块中不执行该代码,可以避免捕获意外错误。 再次,捕捉意外的错误可以隐藏错误。

例如:

try:
    try_this(whatever)
except SomeException as the_exception:
    handle(the_exception)
else: 
    return something

“try,except”套件有两个可选的子句, elsefinally 。 所以它实际上是try-except-else-finally

只有当try块没有异常时,'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方法与其他方法(可能会产生错误)进行比较,我们发现它可以节省代码行,并且具有更易读,可维护,更少错误的代码库。

finally

finally将无论什么执行,即使另一条线路正在用一个return语句进行评估。

用伪代码分解

这可能有助于以最小的可能形式展现所有功能并附带评论。 假设这种语法正确(但不能运行,除非名称被定义)伪代码在函数中。

例如:

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. e.g.:
    return True # hijacks the return in the else clause above

确实,我们可以将代码包含在try块的else块中,而不是在没有异常的情况下运行,但如果代码本身引发了我们正在捕获的那种异常呢? 将它留在try块中会隐藏该错误。

我们希望尽量减少try块中的代码行数量,以避免捕获我们并不期望的异常,原则是如果我们的代码失败,我们希望它失败。 这是最佳做法。

我的理解是,例外不是错误

在Python中,大多数例外都是错误。

我们可以使用pydoc查看异常层次结构。 例如,在Python 2中:

$ python -m pydoc exceptions

或Python 3:

$ python -m pydoc builtins

会给我们的层次结构。 我们可以看到,大多数种类的Exception是错误的,虽然Python使用其中一些喜欢的事情结束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并不赞同这样的观点,即异常只能用于例外情况,实际上这个习惯用法是“请求原谅而不是允许”。 这意味着使用异常作为流量控制的常规部分是完全可以接受的,事实上,这是鼓励的。

这通常是一件好事,因为以这种方式工作有助于避免一些问题(作为一个明显的例子,通常会避免竞争条件),并且它会使代码更具可读性。

想象一下,你有一种情况,你需要处理一些用户输入,但有一个已经处理的默认值。 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

请注意优点。 没有必要检查该值是否有效,并分别解析它们,它们只执行一次。 代码也遵循更合乎逻辑的进程,主要代码路径是第一个,然后是'如果它不起作用,那么做'。

这个例子自然是有点人为的,但是它显示了这种结构的情况。

链接地址: http://www.djcxy.com/p/54675.html

上一篇: Is it a good practice to use try

下一篇: HTML5Boilerplate and the Image Replacement Class text