除了在每个函数中,我们都使用try吗?

2024-05-29 03:33:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我们应该总是用一个try...except块将编写的每个函数括起来吗? 我这样问是因为有时在一个函数中我们会引发Exception,调用这个函数的调用方没有异常

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


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

然后我们的应用程序崩溃。在这个明显的例子中,我很想用try..except将被调用方和调用方括起来。

但是我读了很多Python代码,他们并不是一直这样做的。


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,那会怎么样?我很抱歉把这事搞砸了。但这是我不明白的问题。


Tags: 函数getifdefstderrstdoutexceptionvcs
3条回答

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

…而且因为它们可能不应该发生,在很大程度上,你可以忽略它们。这是件好事。

有时您会执行操作,但特定异常除外,例如,如果执行以下操作:

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)

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

重读这个问题,我怀疑您可能误解了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")

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)。在这种情况下,由于回溯中的行号/信息指向一段并非真正问题的代码,因此会使调试变得更困难。

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

相关问题 更多 >

    热门问题