在python中嵌套的try/except块是一个很好的编程习惯吗?

Mic*_*hal 170 python

我正在编写自己的容器,需要通过属性调用来访问内部的字典.容器的典型用法如下:

dict_container = DictContainer()
dict_container['foo'] = bar
...
print dict_container.foo
Run Code Online (Sandbox Code Playgroud)

我知道写这样的东西可能是愚蠢的,但这是我需要提供的功能.我正在考虑以下列方式实现它:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        try:
            return self.dict[item]
        except KeyError:
            print "The object doesn't have such attribute"
Run Code Online (Sandbox Code Playgroud)

我不确定嵌套的try/except块是否是一个好习惯,所以另一种方法是使用hasattr()has_key():

def __getattribute__(self, item):
        if hasattr(self, item):
            return object.__getattribute__(item)
        else:
            if self.dict.has_key(item):
                return self.dict[item]
            else:
                raise AttributeError("some customised error")
Run Code Online (Sandbox Code Playgroud)

或者使用其中一个和一个尝试catch块,如下所示:

def __getattribute__(self, item):
    if hasattr(self, item):
        return object.__getattribute__(item)
    else:
        try:
            return self.dict[item]
        except KeyError:
            raise AttributeError("some customised error")
Run Code Online (Sandbox Code Playgroud)

哪个选项最pythonic和优雅?

lqc*_*lqc 152

你的第一个例子非常好.即使是官方的Python文档也推荐这种称为EAFP的风格.

就个人而言,我更喜欢在没有必要时避免嵌套:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        pass  # fallback to dict
    try:
        return self.dict[item]
    except KeyError:
        raise AttributeError("The object doesn't have such attribute") from None
Run Code Online (Sandbox Code Playgroud)

PS.has_key()已经在Python 2中被弃用了很长时间.请item in self.dict改用.

  • PEP 20:扁平比嵌套更好. (8认同)
  • `from None'在最后一行意味着什么? (6认同)
  • `return object .__ getattribute __(item)`不正确,由于传递了错误数量的参数,将产生TypeError。相反,它应该是`return object .__ getattribute __(self,item)`。 (2认同)
  • @niklas它基本上抑制了异常上下文("在处理此异常期间发生了另一个异常"-esque消息).见[这里](/sf/answers/1732682521/) (2认同)

Bru*_*ado 15

虽然在Java中使用Exceptions进行流控制确实是一种不好的做法(主要是因为异常迫使jvm收集资源(这里更多)),在Python中你有两个重要的原则:Duck TypingEAFP.这基本上意味着我们鼓励您尝试以您认为可行的方式使用对象,并在事情不那样时进行处理.

总之,唯一的问题是你的代码得到了太多的缩进.如果您愿意,可以尝试简化一些嵌套,如lqc建议的那样


Bre*_*arn 8

对于您的具体示例,您实际上不需要嵌套它们.如果try块中的表达式成功,则函数将返回,因此只有在第一次尝试失败时才会运行整个try/except块之后的任何代码.所以你可以这样做:

def __getattribute__(self, item):
    try:
        return object.__getattribute__(item)
    except AttributeError:
        pass
    # execution only reaches here when try block raised AttributeError
    try:
        return self.dict[item]
    except KeyError:
        print "The object doesn't have such attribute"
Run Code Online (Sandbox Code Playgroud)

嵌套它们并不坏,但我觉得保持平整使结构更清晰:你依次尝试一系列的东西并返回第一个有效的东西.

顺便说一下,您可能想要考虑是否真的想要使用__getattribute__而不是在__getattr__这里.使用__getattr__将简化操作,因为您将知道正常的属性查找过程已经失败.


Rav*_*are 8

嵌套 try/except 的一个简单的好例子如下:

import numpy as np

def divide(x, y):
    try:
        out = x/y
    except:
        try:
            out = np.inf * x / abs(x)
        except:
            out = np.nan
    finally:
        return out

Run Code Online (Sandbox Code Playgroud)

现在尝试各种组合,你会得到正确的结果:

divide(15, 3)
# 5.0

divide(15, 0)
# inf

divide(-15, 0)
# -inf

divide(0, 0)
# nan

Run Code Online (Sandbox Code Playgroud)

(当然,我们有 NumPy,所以我们不需要创建这个函数。)


mar*_*eau 7

在我看来,这将是处理它的最Python的方式,但因为它使你的问题没有实际意义.请注意,这个定义__getattr__()的,而不是__getattribute__()因为这样做意味着它只有应对"特殊"属性被保留在内部字典.

def __getattr__(self, name):
    """only called when an attribute lookup in the usual places has failed"""
    try:
        return self.my_dict[name]
    except KeyError:
        raise AttributeError("some customized error message")
Run Code Online (Sandbox Code Playgroud)

  • @Blckknght:在这种情况下,打印包含两个异常的回溯都没问题.换句话说,我认为你总是不受欢迎的一揽子声明是正确的.在这里的用法中,它将一个`KeyError`变成一个`AttributeError`,并显示回溯中发生的事情是有用和适当的. (3认同)
  • 请注意,在`except`块中引发异常可能会在Python 3中产生令人困惑的输出.那是因为(根据PEP 3134),Python 3将第一个异常("KeyError")跟踪为第二个异常的"上下文"(` AttributeError`),如果它达到顶级,它将打印包含两个异常的回溯.当没有预料到第二个异常时,这可能会有所帮助,但是如果你故意提出第二个异常,这是不可取的.对于Python 3.3,PEP 415增加了通过使用来自None`的`raise AttributeError("whatever")来抑制上下文的能力. (2认同)
  • 我想接受您的回答,但是在问题中我更好奇使用嵌套的 try/catch 块是否是一个好习惯。另一方面,它是最优雅的解决方案,我将在我的代码中使用它。非常感谢马丁。 (2认同)

Sła*_*art 6

请注意-在这种情况下,首先finally要碰到,但也会跳过。

def a(z):
    try:
        100/z
    except ZeroDivisionError:
        try:
            print('x')
        finally:
            return 42
    finally:
        return 1


In [1]: a(0)
x
Out[1]: 1
Run Code Online (Sandbox Code Playgroud)

  • @Michal:仅供参考:对于`a(0)` 执行两个`finally` 块,但只返回父`finally-return`。 (5认同)

Ign*_*ams 5

在 Python 中,请求宽恕比许可更容易。不要担心嵌套的异常处理。

(此外,has*无论如何,几乎总是在掩护下使用异常。)


Bla*_*g23 5

根据文档,最好通过元组或像这样处理多个异常:

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as e:
    print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error: ", sys.exc_info()[0]
    raise
Run Code Online (Sandbox Code Playgroud)

  • 这个答案并没有真正解决最初的问题,但对于阅读它的任何人来说,请注意“裸”,除非最后是一个糟糕的主意(通常),因为它会捕获所有内容,例如 NameError 和 KeyboardInterrupt - 这通常不是你的意思! (2认同)