5

我们是否应该始终将我们编写的每个函数都用一个 try...except 块括起来?我问这个是因为有时我们在一个函数中 raise Exception,而调用这个函数的调用者没有异常

def caller():
   stdout, stderr = callee(....)


def callee():
   ....
    if stderr:
       raise StandardError(....)

然后我们的应用程序崩溃。在这个明显的例子中,我很想用try..except.

但是我已经阅读了很多 Python 代码,但他们并没有一直这样做try..block


def cmd(cmdl):
    try:
        pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
    except Exception, e:
        raise e
    stdout, stderr = pid.communicate()
    if pid.returncode != 0:
        raise StandardError(stderr)
    return (stdout, stderr)

def addandremove(*args,**kwargs):
    target = kwargs.get('local', os.getcwd())
    f = kwargs.get('file', None)
    vcs = kwargs.get('vcs', 'hg')

    if vcs is "hg":
        try:
            stdout, stderr = cmd(['hg', 'addremove', '--similarity 95'])
        except StandardError, e:
            // do some recovery
        except Exception, e:
            // do something meaningful
    return True

真正困扰我的是:

如果有第三个函数addandremove()在其中一个语句中调用,我们是否也用 try..except 块包围调用?如果这个第三个函数有 3 行,并且每个函数调用自己都有一个 try-except 怎么办?我很抱歉建立这个。但这是我不明白的问题。

4

6 回答 6

11

顾名思义,例外是在特殊情况下——实际上不应该发生的事情

..因为它们可能不应该发生,在大多数情况下,你可以忽略它们。这是一件好事。

除了特定的例外,有时您会这样做,例如,如果我这样做:

urllib2.urlopen("http://example.com")

在这种情况下,预期“无法联系服务器”错误是合理的,因此您可能会这样做:

try:
    urllib2.urlopen("http://example.com")
except urllib2.URLError:
    # code to handle the error, maybe retry the server,
    # report the error in a helpful way to the user etc

然而,试图捕捉每一个可能的错误是徒劳的——有无数的事情可能会出错。作为一个奇怪的例子,如果一个模块修改urllib2并删除了urlopen属性会怎么样——没有理智的理由去期待它NameError,并且没有理智的方法可以处理这样的错误,因此您只需让异常向上传播

让您的代码退出并带有回溯是一件好事 - 它使您可以轻松查看问题的根源,以及导致问题的原因(基于异常及其消息),并修复问题的原因,或处理异常正确的位置...

简而言之,只有当你可以对它们做一些有用的事情时才处理异常。如果不是这样,试图处理所有无数可能的错误只会让你的代码更容易出错并且更难修复


在您提供的示例中,try/except 块什么都不做——它们只是重新引发异常,所以它与更整洁的相同:

def cmd(cmdl):
    pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
    stdout, stderr = pid.communicate()
    if pid.returncode != 0:
        raise StandardError(stderr)
    return (stdout, stderr)

# Note: Better to use actual args instead of * and **,
# gives better error handling and docs from help()
def addandremove(fname, local = None, vcs = 'hg'):
    if target is None:
       target = os.getcwd() 

    if vcs is "hg":
        stdout, stderr = cmd(['hg', 'addremove', '--similarity 95'])
    return True

关于我可能期望的唯一与异常处理相关的事情是在找不到“hg”命令时进行处理,由此产生的异常并不是特别具有描述性。所以对于图书馆,我会做这样的事情:

class CommandNotFound(Exception): pass

def cmd(cmdl):
    try:
        pid = Popen(cmdl, stdout=PIPE, stderr=PIPE)
    except OSError, e:
        if e.errno == 2:
            raise CommandNotFound("The command %r could not be found" % cmdl)
        else:
            # Unexpected error-number in OSError,
            # so a bare "raise" statement will reraise the error
            raise

    stdout, stderr = pid.communicate()
    if pid.returncode != 0:
        raise StandardError(stderr)
    return (stdout, stderr)

这只是将可能令人困惑的“OSError”异常包装在更清晰的“CommandNotFound”中。

重读这个问题,我怀疑你可能误解了 Python 异常是如何工作的(“和调用这个函数的调用者没有异常”位,所以希望澄清一下:

调用者函数不需要任何关于子函数可能引发的异常的知识。您可以调用该cmd()函数并希望它正常工作。

假设您的代码在一个mystuff模块中,并且其他人想要使用它,他们可能会这样做:

import mystuff

mystuff.addandremove("myfile.txt")

或者,如果用户没有hg安装,也许他们想给出一个很好的错误消息并退出:

import mystuff

try:
    mystuff.addandremove("myfile.txt")
except mystuff.CommandNotFound:
    print "You don't appear to have the 'hg' command installed"
    print "You can install it with by... etc..."
    myprogram.quit("blahblahblah")
于 2012-07-13T21:52:15.250 回答
6

try/except子句只有在您知道如何处理引发的错误时才真正有用。采取以下程序:

while True:
    n=raw_input("Input a number>")
    try:
       n=float(n)
       break
    except ValueError:
       print ("That wasn't a number!")  #Try again.

但是,您可能具有如下功能:

def mult_2_numbers(x,y):
    return x*y

并且用户可以尝试将其用作:

my_new_list=mult_2_numbers([7,3],[8,7])

用户可以将其放在 try/except 块中,但随后my_new_list不会被定义,并且可能稍后会引发异常(可能是 NameError)。在这种情况下,您会更难调试,因为回溯中的行号/信息指向一段不是真正问题的代码。

于 2012-07-13T21:22:36.220 回答
6

您应该使用 try catch 块,以便您可以专门定位异常的来源。你可以把这些块放在你想要的任何地方,但除非它们产生某种有用的信息,否则没有必要添加它们。

于 2012-07-13T21:16:36.800 回答
1

对于自省类型工具和“异常”处理,您的编码团队需要做出几个编程决策。

使用异常处理的一个好地方是使用操作系统调用,例如文件操作。推理是,例如,一个文件的访问权限可能会受到客户端应用程序的访问限制。该访问限制通常是操作系统管理任务,而不是 Python 应用程序功能。因此,在您的应用程序无法控制的情况下,异常将是一个很好的用途。

您可以将上一段的异常应用更改为更广泛,从某种意义上说,将异常用于您的团队代码无法控制的事物,例如所有“设备”或操作系统资源(如操作系统计时器、符号链接、网络连接) , ETC。

另一个常见的用例是,当您使用一个库或包时,该库或包被设计为通过大量异常,并且该包希望您为此捕获或编码。有些包旨在尽可能少地抛出异常,并期望您根据返回值进行编码。那么你的例外应该很少见。

一些编码团队使用异常作为记录应用程序中“事件”的边缘案例的一种方式。

我发现在考虑是否使用异常时,我要么通过没有大量的 try/except 来编程以最大限度地减少失败,要么让调用例程期望有效的返回值或期望无效的返回值。或者我为失败编程。也就是说,我通过函数对预期的失败进行编程,我使用了很多 try/except 块。考虑为“预期”失败进行编程,例如使用 TCP;数据包没有到达那里,甚至没有按顺序到达,但是通过使用发送/读取重试等使用 TCP 进行异常处理。

就我个人而言,我在尽可能小的块大小周围使用 try-except 块,通常是一行代码。

于 2012-07-13T22:37:32.983 回答
0

由你决定; 主要例外的角色包括(引自这本精彩的书):

  • 错误处理
  • 活动通知
  • 特殊情况处理
  • 终止行动
  • 异常控制流
于 2012-07-13T21:26:04.060 回答
0

当您知道错误是什么时,请使用 try/except 进行调试。否则,您不必对每个函数都使用 try/except。

于 2012-07-13T21:32:55.993 回答